2017-02-15 53 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)的不同結果。