我可以在实际的函数调用中将静态映射定义为函数参数吗?

Can I define a static map as a function argument in the actual function call?

本文关键字:定义 映射 函数 参数 静态 函数调用 我可以      更新时间:2023-10-16

UPDATE:我刚刚对各种解决方案的核心转换逻辑进行了一些基准测试。以下是单个迭代的CPU时间(以纳秒为单位),在Wandbox上进行500万次迭代的平均值:

8 - switch语句

3800 -动态地图

您可以看到,每次创建一个新的映射(动态映射)是相对耗时的。创建一次映射(静态映射),然后只引用它,几乎比快22倍。但是,巧合的是,原来基于交换机的解决方案比静态映射快22倍 !

嗯…我从未考虑过动态映射是可行的解决方案,但对静态映射抱有希望。它比好的老式switch语句慢了很多,但是8纳秒和176纳秒真的重要吗?

更新结束


核心问题我试图解决是从一个"命名空间"转换到另一个(在一般意义上,不是c++ namespace关键字)。这在编程中很常见,通常在c++中使用switch语句来解决,如:

#include <iostream>
enum MUSIC { ROCK, RAP, EDM, COUNTRY };
enum COLOR { RED, BLUE, ORANGE, WHITE };
COLOR convert(MUSIC music)
{
    COLOR color = WHITE;
    switch (music) {
    case RAP: color = RED; break;
    case EDM: color = BLUE; break;
    case ROCK: color = RED; break;
    }
    return color;
}
int main()
{
    COLOR c = convert(COUNTRY);
    std::cout << c << std::endl;
}

下面是我的解决方案(模板将隐藏在头文件的某个地方)。convert()函数适用于对std::map有效的任何类型,例如,enumenumlongstd::string。第一个参数是键或"from"值,第二个参数是不能转换的默认值,返回值是映射值或"to"值。(特别感谢@Yakk对模板参数推导的帮助。)

#include <iostream>
#include <map>
#include "boost/assign.hpp"
template<class T> struct no_deduction { typedef T type; };
template<typename Key, typename T>
T convert(const Key &k, const T &d, const typename no_deduction<std::map<Key, T> >::type &m) {
    typename std::map<Key, T>::const_iterator it = m.find(k);
    return it == m.end() ? d : it->second;
}
using boost::assign::map_list_of;
enum MUSIC { ROCK, RAP, EDM, COUNTRY };
enum COLOR { RED, BLUE, ORANGE, WHITE };
int main()
{
    COLOR c = convert(COUNTRY, WHITE, map_list_of (RAP, RED) (EDM, BLUE) (ROCK, RED));
    std::cout << c << std::endl;
}
无论如何,请注意上面的map_list_of每次调用时都会创建相同的列表。我希望它是静态。这是显而易见的解决方案:
    static const std::map<MUSIC, COLOR> m = map_list_of (RAP, RED) (EDM, BLUE) (ROCK, RED);
    COLOR c = convert(COUNTRY, WHITE, m);

但是我试图使它使用起来简单,特别是,作为一个单一的语句。这里有一个解决方案,但我也宁愿避免它。

#define CONVERT(f,t,d,m) do{static const std::map<BOOST_TYPEOF(f), BOOST_TYPEOF(d)> m_ =
    map_list_of m; t = convert(f, d, m_);}while(0)
    COLOR c;
    CONVERT(COUNTRY, c, WHITE, (RAP, RED) (EDM, BLUE) (ROCK, RED));

有没有人知道一些c++ 魔法(实际上是c++ 03)可以调用我的convert()函数

    带有静态地图的
  1. 作为单个语句,而
  2. 不使用宏?

为什么不直接使用静态数组并对其进行索引呢?

enum MUSIC { ROCK = 0, RAP = 1, EDM = 2, COUNTRY = 3 };
enum COLOR { RED, BLUE, ORANGE, WHITE };
static const COLOR music_to_color[] = 
{
    RED,     //maps to 'ROCK = 0'
    BLUE,    //maps to 'RAP = 1'
    ORANGE,  //maps to 'EDM = 2'
    WHITE    //maps to 'COUNTRY = 3'
};
MUSIC music = RAP;
std::cout << music_to_color[music] << std::endl;

在这种情况下,convert函数的主体只能是return music_to_color[music](其中music将是参数)。这种方法的优点是速度快,并且没有创建类似std::map的额外运行时开销。缺点是你不能轻易地在运行时修改映射,而且它更像"C"而不是"c++"(如果你需要c++接口:迭代器等,你可以使用std::array代替静态数组来减轻痛苦)

另一种方法是使用模板专门化,但这需要大量的输入,如果有很多值要映射,则很难维护。

大写名称通常是为宏保留的,所以我重命名了它们。

在某些时候,您必须编写如何将一种枚举类型转换为另一种枚举类型的工作。使用map最简单的方法如下:

Color convert(Music music)
{
    static const std::map<Music, Color> converter = { { Rap, Red }, { Edm, Blue }, { Rock, Red } };
    return converter.at(music);
}

使用初始化列表,这是c++ 11的特性。如果你不能使用,你可以试试:

Color convert(Music music)
{
    static const struct Once
    {
        Once()
        {
            converter[Rap] = Red;
            converter[Edm] = Blue;
            converter[Rock] = Red;
        }
        std::map<Music, Color> converter;
    } once;
    std::map<Music, Color>::const_iterator find_it = once.converter.find(music);
    assert(find_it != once.converter.end());
    return find_it->second;
}

这段代码可能没有使用花哨的宏,但它是安全的、相当高效的(map的一个实例)和可读的(尽管我确实对该结构体做了一些技巧)。有更快的方法,但它们更复杂,可能不是你用例所必需的。

如果枚举值少于12个,我会考虑只使用switch语句。它通常比map快。