static_assert总是在constexpr中触发

时间:2016-08-29 10:45:25

标签: c++ c++14 variadic-templates constexpr static-assert

我编写了以下代码来查找包含给定类型的元组中的第一个索引。

#include <cstdio>
#include <tuple>
#include <type_traits>
#include <utility>

namespace detail {
    template <typename T, typename Tuple, std::size_t H, std::size_t... I>
    constexpr std::size_t tuple_type_index_impl(Tuple tup, std::index_sequence<H, I...> seq, std::size_t ret = 0) {
        if (std::is_same<T, typename std::tuple_element<H, Tuple>::type>::value)
            ret = H;
        else ret = tuple_type_index_impl<T>(tup, std::index_sequence<I...>{});
        return ret;
    }

    template <typename T, typename Tuple, std::size_t H>
    constexpr std::size_t tuple_type_index_impl(Tuple tup, std::index_sequence<H> seq) {
        static_assert(std::is_same<T, typename std::tuple_element<H, Tuple>::type>::value, "type not in tuple!");
        return H;
    }
}

template <typename T, typename Tuple>
constexpr std::size_t tuple_type_index(Tuple tup) {
    return detail::tuple_type_index_impl<T>(tup, std::make_index_sequence<std::tuple_size<Tuple>::value>{});
}

class a {};
class b {};
class c {};
class d {};

std::tuple<a, b, c> abc;

int main() {

    printf("b is index : %zu\n", tuple_type_index<d>(abc));

    system("pause");
    return 0;
}

我遇到的问题是,如果您尝试查找任何类型的索引,但c会触发static_assert。当你试图找到类型ab的索引时,我甚至不确定为什么它会一直到结束函数。

如果删除static_assert返回值是正确的,但它将返回不在元组中的类型的值。

受到明确回答的启发,我已经重新设计了我的实施。

It is available on coliru

此版本应适用于clang ++和g ++,而不仅仅适用于MSVC。

1 个答案:

答案 0 :(得分:1)

我认为问题在于你没有写一个正确的终端(索引序列为空)tuple_type_index_impl()函数。

En passant,0值(在“未找到类型”的情况下)不是一个好主意(恕我直言),因为你可能会混淆“第一类”。

我已在以下

中修改了您的示例
#include <tuple>
#include <utility>
#include <iostream>
#include <type_traits>

namespace detail
 {
   template <typename T, typename Tuple>
      constexpr std::size_t tuple_type_index_impl
         (const Tuple &,
          const std::index_sequence<> &,
          std::size_t ret = std::tuple_size<Tuple>::value)
       { return ret; }

   template <typename T, typename Tuple, std::size_t H, std::size_t ... I>
      constexpr std::size_t tuple_type_index_impl
         (const Tuple & tup,
          const std::index_sequence<H, I...> & seq,
          std::size_t ret = std::tuple_size<Tuple>::value)
       {
         return std::is_same<T, typename std::tuple_element<H, Tuple>::type>::value
            ? H
            : tuple_type_index_impl<T>(tup, std::index_sequence<I...>{}, ret);
       }
 }

template <typename T, typename Tuple>
constexpr std::size_t tuple_type_index (const Tuple & tup)
 {
   return detail::tuple_type_index_impl<T>
      (tup, std::make_index_sequence<std::tuple_size<Tuple>::value>{});
 }

class a {};
class b {};
class c {};
class d {};

int main()
 {
   std::tuple<a, b, c> abc;

   std::cout << "a is index: " << tuple_type_index<a>(abc) << std::endl;
   std::cout << "b is index: " << tuple_type_index<b>(abc) << std::endl;
   std::cout << "c is index: " << tuple_type_index<c>(abc) << std::endl;
   std::cout << "d is index: " << tuple_type_index<d>(abc) << std::endl;

   return 0;
 }

给我一​​些时间,我试着准备一个更简单的例子。

---编辑:添加示例---

您正在使用类型,因此不需要创建和传递给函数元组(和索引序列)对象。

我已经准备了一个基于(基于SFINAE)tupleTypeIndexHelper结构的示例;正在使用类型,因此您使用

tuple_type_index<a, std::tuple<a, b, c>>()

或(当abcstd::tuple<a, b, c>的同义词时)

tuple_type_index<b, decltype(abc)>()

以下示例也适用于(至少:使用g ++ 4.9.2和使用clang ++ 3.5)和C ++ 11

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

namespace detail
 {
   template <typename T, typename Tuple, std::size_t Ind>
      struct tupleTypeIndexHelper
       {
         static constexpr std::size_t  dimT { std::tuple_size<Tuple>::value };

         template <std::size_t I = Ind>
            static typename std::enable_if<(I >= dimT), std::size_t>::type func ()
          { return dimT; }

         template <std::size_t I = Ind>
            static typename std::enable_if<(I < dimT), std::size_t>::type func ()
          {
            using typeI = typename std::tuple_element<I, Tuple>::type;

            return std::is_same<T, typeI>::value
               ? I
               : tupleTypeIndexHelper<T, Tuple, I+1U>::func();
          }
       };

 }

template <typename T, typename Tuple>
constexpr std::size_t tuple_type_index ()
 { return detail::tupleTypeIndexHelper<T, Tuple, 0U>::func(); }

class a {};
class b {};
class c {};
class d {};

int main()
 {
   using  t3 = std::tuple<a, b, c>;

   std::cout << "a is index:   " << tuple_type_index<a, t3>() << std::endl;
   std::cout << "b is index:   " << tuple_type_index<b, t3>() << std::endl;
   std::cout << "c is index:   " << tuple_type_index<c, t3>() << std::endl;
   std::cout << "d is index:   " << tuple_type_index<d, t3>() << std::endl;
   std::cout << "int is index: " << tuple_type_index<int, t3>() << std::endl;

   std::tuple<a, b, c> abc;

   std::cout << "a is index: " << tuple_type_index<a, decltype(abc)>() << std::endl;
   std::cout << "b is index: " << tuple_type_index<b, decltype(abc)>() << std::endl;
   std::cout << "c is index: " << tuple_type_index<c, decltype(abc)>() << std::endl;
   std::cout << "d is index: " << tuple_type_index<d, decltype(abc)>() << std::endl;

   return 0;
 }