C ++ 1z - 如果在模板展开期间发生条件,则抛出编译错误

时间:2015-12-05 15:00:48

标签: c++ templates c++17

我正在编写一个模板,用于定义类型参数包中给出的类型,其位置与传递给模板的数字相同。这就是我写的:

template<size_t I, typename T, typename... Ts> 
struct get_type {
    typedef typename std::conditional<I == 0, T, typename get_type<I-1, Ts...>::type>::type type;
};

template<size_t I, typename T>
struct get_type<I, T> {
    // This works only if compiler stops unfolding the template when I == 0
    static_assert(I == 0, "get_type - index out of bounds");
    typedef T type;
};

这种方法有问题,如果我们编写这样的代码:

static_assert(std::is_same<double, get_type<1,int,double,float>::type>::value, "I wanted double!");

编译器仍然“展开”模板到最后(即使它应该知道直到那时的类型,恰好在I等于0时),最后I溢出并且不再等于0,这意味着static_assert抛出错误,索引I超出范围。但是我仍然想在编译时抛出一个错误,如果I是TRULLY超出界限的话。有没有办法做到这一点?

2 个答案:

答案 0 :(得分:1)

编译器必须展开模板,否则它不知道type的类型是什么。

如果索引超出范围,

std::tuple_element_t已经给出了(相当冗长的)错误。

template<size_t I, typename... Ts>
using get_type_t = std::tuple_element_t<I, std::tuple<Ts...>>;

通过与显式边界检查一起使用,可以产生更直观的错误消息:

template<size_t I, typename... Ts>
struct get_type {
    using L=std::tuple<Ts...>;
    static_assert(I < 0 || I >= std::tuple_size<L>(), "out of bounds");
    using type = std::tuple_element_t<I, L>;
};

template<size_t I, typename... Ts>
using get_type_t = typename get_type<I, Ts...>::type;

这是一个没有std::tuple(改编自boostcon)开销的例子:

struct empty{};

template<class T>
struct tag_t:empty{};

template<class T>
tag_t<T> tag{};

template <typename ignore>
struct lookup;

template <std::size_t... ignore>
struct lookup<std::index_sequence<ignore...>> {
    template <typename nth>
    static nth
    apply(decltype(ignore, empty())..., tag_t<nth>, ...);
};

template<std::size_t I, class... Ts>
using get_type = decltype(
    lookup<std::make_index_sequence<I>>::apply(tag<Ts>...)
);

// Test
static_assert(std::is_same<get_type<1, int, float, int>, float>(), "");
static_assert(std::is_same<get_type<0, int, float, int>, int>(), "");

答案 1 :(得分:0)

这是我想出的答案:

struct error_type {}; 

template<size_t I, typename T, typename... Ts> 
struct get_type {
   typedef typename std::conditional<I == 0, T, typename get_type<I-1, Ts...>::type>::type type;
   static_assert(!std::is_same<type, error_type>::value, "get_type - index out of bounds");
};

template<size_t I, typename T>
struct get_type<I, T> {
    typedef typename std::conditional<I == 0, T, error_type>::type type;
};

但它似乎有点冗长(我们正在创建一个虚拟结构),所以我留下这个问题以防万一有人提出比我的解决方案更聪明的东西......