2017-02-15 52 views
6

我希望能够通过类型的ID创建switch语句。我发现了一种机制,可以为不同类型提供唯一的ID。这很简单:类型的C++ constexpr值

template <typename T> 
struct type { 
    static void id() { } 
}; 

template <typename T> 
constexpr const size_t type_id() { 
    return reinterpret_cast<size_t>(&type<T>::id); 
} 

我认为这将评估为一个常数,我可以用作开关的情况。但是,我得到一个错误的情况下表达,不是我做了以下的常数:

int main(void) { 
    size_t a = type_id<int>(); 
    switch (a) { 
    case type_id<int>(): 
     break; 
    } 
    return 0; 
} 

为什么它不是一个常数?我怎么能达到这个效果?

编辑:

我可以做这样的事情,而不reinterpret_cast的呢?

+1

'reinterpret_cast' * *不能出现在'constexpr'函数中。就是这样。 – DeiDei

+1

'ysize'是什么?发布[MCVE]。 –

+0

@LightnessRacesinOrbit对不起,ysize是我自己定义的size_t。编辑 –

回答

3

发现我不知道这是一个好主意,但......只是为了好玩...使用constexpr计数器,建议in this page,你应该能够替代的价值指针。

以下是(我再说一遍:只是为了好玩)全实验

#include <iostream> 

template <int N> 
struct flag 
{ friend constexpr int adl_flag (flag<N>); }; 

template <int N> 
struct writer 
{ 
    friend constexpr int adl_flag (flag<N>) 
    { return N; } 

    static constexpr int value { N }; 
}; 

template <int N, int = adl_flag (flag<N> {})> 
int constexpr reader (int, flag<N>) 
{ return N; } 

template <int N> 
int constexpr reader (float, flag<N>, int R = reader (0, flag<N-1> {})) 
{ return R; } 

int constexpr reader (float, flag<0>) 
{ return 0; } 

template <int N = 1> 
int constexpr next (int R = writer<reader (0, flag<32> {}) + N>::value) 
{ return R; } 

template <typename T> 
struct type 
{ 
    static constexpr int id { next() }; 

    constexpr static int type_id() 
    { return id; } 
}; 

void printType (int idT) 
{ 
    switch (idT) 
    { 
     case type<int>::type_id(): 
     std::cout << "- int type" << std::endl; 
     break; 

     case type<long>::id: 
     std::cout << "- long type" << std::endl; 
     break; 

     default: 
     std::cout << "- another type" << std::endl; 
     break; 
    } 
} 

int main() 
{ 
    int ii { type<int>::id }; 
    int il { type<long>::type_id() }; 

    printType(ii); 
    printType(il); 
} 
+0

这个和@ PaperBirdMaster的都很棒,可惜我不能接受2个答案。谢谢,聪明! –

+0

http://cpp.sh/6beocj提供了一个错误。 – neckTwi

2

这可能会解决你的问题:

#include <tuple> 

//Index from http://stackoverflow.com/a/18063608/3484570 
template <class T, class Tuple> 
struct Index; 

template <class T, class... Types> 
struct Index<T, std::tuple<T, Types...>> { 
    static const std::size_t value = 0; 
}; 

template <class T, class U, class... Types> 
struct Index<T, std::tuple<U, Types...>> { 
    static const std::size_t value = 1 + Index<T, std::tuple<Types...>>::value; 
}; 

template <class T> 
constexpr std::size_t type_id() { 
    //need to add every type in this tuple: 
    return Index<T, std::tuple<int, double, char>>::value; 
} 

int main() { 
    size_t a = type_id<int>(); 
    switch (a) { 
     case type_id<int>(): 
      break; 
    } 
} 

好消息是,你得到一个type_id<T>(),你可以在constexpr上下文中使用,如您想要的case
坏消息是你需要列出所有支持的类型。
实际上,您可能会习惯于在您要求type_id不支持的类型时发生的错误消息,只需添加它并最终添加所有相关类型即可。

2

我想建议另一种方法涉及constexpr函数和宏(eeeewww ...):

// Some naive text hashing function 
template <std::size_t SIZE> 
constexpr std::size_t hash(const char (&type_name)[SIZE]) 
{ 
    std::size_t result{0xf}; 
    for (const auto &c : type_name) 
    { 
     result <<= 1; 
     result |= c; 
    } 

    return result; 
} 

首先我们创建一个constexpr功能能够将字符串文字转换成一个数字,这个是我的做法,但只要它是constexpr你可以选择anoter功能,那么我们创建一个使用#该字符串化给定参数的宏:

#define TYPE_ID(X) hash(#X) 

而现在,我们可以用它:

int main(void) { 
    size_t a = TYPE_ID(int); 
    switch (a) { 
    case TYPE_ID(int): 
     break; 
    } 
    return 0; 
} 

优点:

  • 非常简单。
  • 微量的代码。

缺点:

  • 宏。
  • 接受任何值,包括废话:TYPE_ID(I LOVE BACON)是有效的。
  • 即使它们可能是相同类型,也会产生TYPE_ID(size_t)TYPE_ID(unsigned long)的不同结果。