更新:我刚刚对各种解决方案的核心转换逻辑进行了一些基准测试。以下是单次迭代的纳秒的CPU时间,平均为Wandbox上的500万次迭代:
8 - 切换声明
176 - 静态地图
3800 - 动态地图
如您所见,每次创建新地图(动态映射)都相对耗时。创建一次地图(静态映射)并在此后仅参考它几乎 22倍。但巧合的是,最初的基于交换机的解决方案比静态映射<22>快!
嗯......我从未考虑过动态映射是一种可行的解决方案,但希望能够进行静态映射。它比良好的旧switch
语句慢得多,但是8纳秒与176纳秒真的重要吗?
更新结束
核心问题我试图解决的是从一个&#34;命名空间&#34;到另一个(在一般意义上,不是C ++ namespace
关键字)。这在编程中很常见,通常使用switch
语句在C ++中解决,如下所示:
#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
有效的任何类型,例如enum
到enum
和long
到std::string
。第一个参数是键,或者&#34;来自&#34;值,第二个是默认值,如果不能转换,返回值是映射,或者&#34;到,&#34;值。 (特别感谢@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));
有没有人知道可以调用我的convert()
函数的某些C ++ magic (实际上是C ++ 03)
答案 0 :(得分:2)
大写名称通常是为宏保留的,所以我重命名了它们。
在某些时候,你将不得不完成编码一个枚举类型如何转换为另一个的工作。使用地图的最简单方法如下:
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;
}
这段代码可能不会使用花哨的宏,但它安全,合理有效(地图的一个实例)和可读(虽然我确实在那里用那个结构做了一个技巧)。有更快的方法,但它们更复杂,可能不是您的用例所必需的。
如果枚举值少于十几个,我会考虑使用switch语句。它通常比地图更快。
答案 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 ++ like”更像“C-like”(如果需要C ++接口,可以使用std::array
而不是静态数组来缓解这种痛苦:迭代器等。)
另一种做同样事情的方法是使用模板特化,但这是很多打字,如果你有很多要映射的值,就很难维护。