【问题标题】:Want kind of constexpr switch case on type想要类型上的 constexpr 开关盒
【发布时间】:2017-04-06 09:23:04
【问题描述】:

我目前正在使用这个技巧来获得基于类型的 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
        >
      >
    >
  >;
}

但是这段代码不是很好看,如果我们想检查更多的类型,这可能是不可读的。是否可以像有一个 switch case 块一样做同样的事情?

实际上,代码比这更复杂,因为 std::conditional 需要一些类型,所以我们需要一些类来解决问题:

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

例如。

【问题讨论】:

    标签: c++ templates metaprogramming


    【解决方案1】:

    我认为使用模板专业化会更容易


    示例代码:

    #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
    }
    

    【讨论】:

    • 谢谢,我喜欢你的解决方案。我没有考虑模板专业化;我不习惯玩模板。
    • 不仅更容易,而且更好,因为无效参数最终会导致编译器错误而不是 nullptr,或者至少很容易做到这一点
    【解决方案2】:

    您无需求助于元编程,简单的 ifs 就可以了:

    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

    【讨论】:

    • Visual C++ 说 'constexpr' 函数只能有一个返回语句
    • @Boiethios 你有我的同情。我在这里坚持使用 VC++ 2008 ;)
    • 这个答案可以通过使用?: 运算符简单地重写为单个return 语句。
    • @hvd:好的,但是你基本上又回到了原来的嵌套混乱。
    • @hvd: 那是嵌套的
    【解决方案3】:

    您可以创建 constexpr 字符串数组和列表类型的元组来创建映射 list type -&gt; index -&gt; name(如果您需要映射 index -&gt; 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]

    【讨论】:

      猜你喜欢
      • 2022-12-15
      • 1970-01-01
      • 2018-08-04
      • 1970-01-01
      • 1970-01-01
      • 2010-09-29
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多