【问题标题】:Get index of a tuple element's type?获取元组元素类型的索引?
【发布时间】:2013-08-05 16:49:23
【问题描述】:

如果我有一个具有不同元素类型的元组,例如

std::tuple<T0, T1, T2, ...>

以及如何获取元素类型的索引?

template<class T, class Tuple>
struct Index
{
    enum {value = ?;}
};

谢谢。

【问题讨论】:

  • 如果你有std::tuple&lt;int, float, int, std::string&gt; 并且你要求int 会发生什么?还有你为什么需要这个?
  • 我假设元组有不同的元素类型。如果没有这样的假设,得到任何一个匹配就可以了。我用它通过给定的基类的派生类元组来实现一个高效的抽象工厂。
  • 您是在为特定环境寻找它,还是只为一般 C++ 寻找它?
  • 无特定环境。

标签: c++ templates tuples


【解决方案1】:
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;
};

See it live at Coliru.

此实现返回给定类型第一次出现的索引。请求不在元组中的类型的索引会导致编译错误(并且是一个相当丑陋的错误)。

【讨论】:

  • 如果某个类型不在元素类型中,那么行为是什么?
  • @user1899020 Compile-time error.
  • @user1899020 尝试更改实际表达式中的类型,而不是输出字符串。
  • @Casey A static_assert 可以帮助解决神秘的错误消息。
  • @Casey 与 coliru 的链接似乎已损坏,您能否将代码提供给 Try It Online!?他们的分享链接不会中断 :)
【解决方案2】:
template< size_t I, typename T, typename Tuple_t>
constexpr size_t index_in_tuple_fn(){
    static_assert(I < std::tuple_size<Tuple_t>::value,"The element is not in the tuple");

    typedef typename std::tuple_element<I,Tuple_t>::type el;
    if constexpr(std::is_same<T,el>::value ){
        return I;
    }else{
        return index_in_tuple_fn<I+1,T,Tuple_t>();
    }
}

template<typename T, typename Tuple_t>
struct index_in_tuple{
    static constexpr size_t value = index_in_tuple_fn<0,T,Tuple_t>();
};

上面的例子避免了生成大量子元组,当你为大元组调用index_in_tuple时,这会导致编译失败(内存不足)

【讨论】:

    【解决方案3】:

    使用constexpr“function”(或 lambda),您可能会这样做

    template <class T, class Tuple>
    struct Index;
    
    template <class T, typename... Ts>
    struct Index<T, std::tuple<Ts...>>
    {
    
        static constexpr std::size_t index = [](){
            constexpr std::array<bool, sizeof...(Ts)> a{{ std::is_same<T, Ts>::value... }};
    
            // You might easily handle duplicate index too (take the last, throw, ...)
            // Here, we select the first one.
            const auto it = std::find(a.begin(), a.end(), true);
    
            // You might choose other options for not present.
    
            // As we are in constant expression, we will have compilation error.
            // and not a runtime expection :-)
            if (it == a.end()) throw std::runtime_error("Not present");
    
            return std::distance(a.begin(), it);
        }();
    };
    

    实际上需要 C++20 作为 std 函数缺少的 constexpr, 但可以很容易地为以前的版本重写。 (对于 constexpr 的严格限制,C++11 会更加棘手)。

    【讨论】:

      【解决方案4】:

      另一个使用折叠表达式。 它还会在未找到时将值设置为 -1。

      template <class X, class Tuple>
      class Idx;
      
      template <class X, class... T>
      class Idx<X, std::tuple<T...>> {
          template <std::size_t... idx>
          static constexpr ssize_t find_idx(std::index_sequence<idx...>) {
              return -1 + ((std::is_same<X, T>::value ? idx + 1 : 0) + ...);
          }
      public:
          static constexpr ssize_t value = find_idx(std::index_sequence_for<T...>{});
      };
      

      直播:https://onlinegdb.com/SJE8kOYdv

      编辑:

      正如@Jarod42 所建议的,可以使用 std::max:

      template <class X, class Tuple>
      class Idx;
      
      template <class X, class... T>
      class Idx<X, std::tuple<T...>> {
          template <std::size_t... idx>
          static constexpr ssize_t find_idx(std::index_sequence<idx...>) {
              return std::max({static_cast<ssize_t>(std::is_same_v<X, T> ? idx : -1)...});
          }
      public:
          static constexpr ssize_t value = find_idx(std::index_sequence_for<T...>{});
      };
      template<typename X, class Tuple>
      inline constexpr ssize_t Idx_v = Idx<X, Tuple>::value;
      

      如果类型重复,这个版本返回最后一个的索引。

      直播:https://onlinegdb.com/WenEBQs0L

      【讨论】:

      • 我不确定我是否理解您的建议,在这种情况下 std::min(...) 将始终返回 0,不是吗?使用 max 可以,但在重复的情况下给出最后一个的索引(这可能足够好)
      • 哎呀,确实,评论主要是为了突出重复的问题;最后一个的最大值可能没问题。
      【解决方案5】:
      template <typename T, typename U, typename... Us>
      constexpr auto getIndex() {
          if constexpr (is_same_v<T, U>) {
              return 0;
          } else {
              if constexpr (sizeof...(Us)) {
                  return 1 + getIndex<T, Us...>();
              } else {}
          }
      }
      
      template <typename T, typename U, typename... Us>
      constexpr auto getIndex(const tuple<U, Us...> &) {
          return getIndex<T, U, Us...>();
      }
      

      用法

      tuple the_tuple{'\0', 1, 2L, 3.0, "4", string{"5"}};
      cout << getIndex<char>(the_tuple) << endl; // 0
      cout << getIndex<double>(the_tuple) << endl; // 3
      cout << getIndex<const char *>(the_tuple) << endl; // 4
      cout << getIndex<string>(the_tuple) << endl; // 5
      /* cout << getIndex<short>(the_tuple) << endl; // compile error */
      

      【讨论】:

        【解决方案6】:

        试试这个,如果元组为空,T不存在或元组中不唯一,则报错:

        template <template <typename ...> class TT, typename ...Ts>
        struct defer
        {
            using type = TT<Ts...>;
        };
        
        template <typename, typename, typename>
        struct tuple_index_helper;
        
        template <std::size_t I, typename T, typename U, typename ...Vs>
        struct tuple_index_helper<std::integral_constant<std::size_t, I>, T, std::tuple<U, Vs...>>
        {
            static_assert(!std::is_same_v<T, U>, "Type not unique.");
            using index = tuple_index_helper<std::integral_constant<std::size_t, I>, T, std::tuple<Vs...>>::index;
        };
        
        template <std::size_t I, typename T>
        struct tuple_index_helper<std::integral_constant<std::size_t, I>, T, std::tuple<>>
        {
            using index = std::integral_constant<std::size_t, I>;
        };
        
        template <typename, typename, typename>
        struct tuple_index;
        
        template <std::size_t I, typename T, typename U, typename ...Vs>
        struct tuple_index<std::integral_constant<std::size_t, I>, T, std::tuple<U, Vs...>>
        {
            using index = std::conditional_t<std::is_same_v<T, U>,
                                             defer<tuple_index_helper, std::integral_constant<std::size_t, I>, T, std::tuple<Vs...>>,
                                             defer<tuple_index, std::integral_constant<std::size_t, I + 1>, T, std::tuple<Vs...>>>::type::index;
        };
        
        template <std::size_t I, typename T>
        struct tuple_index<std::integral_constant<std::size_t, I>, T, std::tuple<>>
        {
            static_assert(!(I == 0), "Empty tuple.");
            static_assert(!(I != 0), "Type not exist.");
        };
        
        template <typename T, typename U>
        inline constexpr std::size_t tuple_index_v = tuple_index<std::integral_constant<std::size_t, 0>, T, U>::index::value;
        

        例子:

        std::tuple<int, float, const char*> t1{};
        std::tuple<int, float, int> t2{};
        std::tuple<> t3{};
        
        constexpr auto idx = tuple_index_v<float, decltype(t1)>;           // idx = 1
        // constexpr auto idx2 = tuple_index_v<long long, decltype(t1)>    // Error: Type not exist.
        // constexpr auto idx3 = tuple_index_v<int, decltype(t2)>          // Error: Type not unique.
        // constexpr auto idx4 = tuple_index_v<int, decltype(t3)>          // Error: Empty tuple.
        

        【讨论】:

          猜你喜欢
          • 2018-08-28
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2016-06-25
          • 1970-01-01
          • 2014-12-15
          • 1970-01-01
          相关资源
          最近更新 更多