2016-11-22 20 views
16

constexpr開關情況我目前在做這一招有基於一種類型的CString:想得到怎樣的類型

template<class ListT> static char constexpr * GetNameOfList(void) 
{ 
    return 
    std::conditional< 
    std::is_same<ListT, LicencesList>::value, "licences", 
    std::conditional< 
     std::is_same<ListT, BundlesList>::value, "bundles", 
     std::conditional< 
     std::is_same<ListT, ProductsList>::value, "products", 
     std::conditional< 
      std::is_same<ListT, UsersList>::value, "users", 
      nullptr 
     > 
     > 
    > 
    >; 
} 

但是這個代碼是不是很好看,如果我們要檢查更多類型,這可能是不可讀的。是否有辦法像做一個開關盒一樣做同樣的事情?

事實上,代碼是比這更復雜,因爲的std ::條件需要某種類型的,所以我們需要一些類這樣的伎倆:

struct LicenceName { static char constexpr * value = "licences"; }; 

例如。

回答

21

我認爲它會使用模板特


示例代碼簡單:

#include <iostream> 
struct A{}; 
struct B{}; 
struct C{}; 
struct D{}; 

template<typename T> constexpr const char* GetNameOfList(); 
//here you may want to make it return nullptr by default 

template<>constexpr const char* GetNameOfList<A>(){return "A";} 
template<>constexpr const char* GetNameOfList<B>(){return "B";} 
template<>constexpr const char* GetNameOfList<C>(){return "C";} 

int main(){ 
    std::cout << GetNameOfList<A>() << '\n'; 
    std::cout << GetNameOfList<B>() << '\n'; 
    std::cout << GetNameOfList<C>() << '\n'; 
    //std::cout << GetNameOfList<D>() << '\n'; //compile error here 
} 
+0

謝謝你,我喜歡你的解決方案。我沒有想過模板專業化;我不習慣玩模板。 – Boiethios

+2

不僅更簡單,而且更好,因爲無效參數以編譯器錯誤而不是nullptr結束,或者至少很容易使其失效 – Justin

13

你並不需要求助於元編程,純if的工作就好了:

template<class ListT> 
constexpr char const *GetNameOfList() { 

    if(std::is_same<ListT, A>::value) return "A"; 
    if(std::is_same<ListT, B>::value) return "B"; 
    if(std::is_same<ListT, C>::value) return "C"; 
    if(std::is_same<ListT, D>::value) return "D"; 

    return nullptr; 
} 

See it live on Coliru

+1

Visual C++聲明**'constexpr'函數只能有一個返回值聲明** – Boiethios

+0

@Boiethios哪個版本的VC++? – Quentin

+0

VC++ 2015更新3(我無法更改編譯器選項) – Boiethios

3

您可以創建constexpr字符串數組以及列表類型的元組來創建映射list type -> index -> name(如果您需要映射index -> types containing strings只是使用元組而不是數組)。 C++ 17做法可能如下所示:

#include <type_traits> 
#include <tuple> 
#include <utility> 
#include <iostream> 

struct LicencesList{}; 
struct BundlesList{}; 
struct ProductsList{}; 
struct UsersList{}; 

using ListTypes = std::tuple<LicencesList, BundlesList, ProductsList, UsersList>; 
constexpr const char *NameList[] = {"licences", "bundles", "products", "users"}; 

template <class Tup, class, class = std::make_index_sequence<std::tuple_size<Tup>::value>> 
struct index_of; 

template <class Tup, class T, std::size_t... Is> 
struct index_of<Tup, T, std::index_sequence<Is...>> { 
    static constexpr std::size_t value = ((std::is_same<std::tuple_element_t<Is, Tup>, T>::value * Is) + ...); 
}; 


template<class ListT> static const char constexpr * GetNameOfList(void) { 
    return NameList[index_of<ListTypes, ListT>::value]; 
} 

int main() { 
    constexpr const char *value = GetNameOfList<BundlesList>(); 
    std::cout << value << std::endl; 
} 

[live demo]

如果你想保持C++ 11兼容的做法是隻長一點(我這裏使用的Casey's answer實現index_of結構):

#include <type_traits> 
#include <tuple> 
#include <iostream> 

struct LicencesList{}; 
struct BundlesList{}; 
struct ProductsList{}; 
struct UsersList{}; 

using ListTypes = std::tuple<LicencesList, BundlesList, ProductsList, UsersList>; 
constexpr const char *NameList[] = {"licences", "bundles", "products", "users"}; 

template <class Tuple, class T> 
struct index_of; 

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

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


template<class ListT> static const char constexpr * GetNameOfList(void) { 
    return NameList[index_of<ListTypes, ListT>::value]; 
} 

int main() { 
    constexpr const char *value = GetNameOfList<BundlesList>(); 
    std::cout << value << std::endl; 
} 

[live demo]