C++1z-如果在模板展开过程中出现条件,则引发编译错误

C++1z - Throwing compile error if condition occurs during template unfolding

本文关键字:条件 错误 编译 如果 过程中 C++1z-      更新时间:2023-10-16

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

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确实超出了范围,我仍然希望在编译时以某种方式抛出一个错误。有办法做到这一点吗?

编译器必须展开模板,否则它将不知道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>(), "");

以下是我得出的答案:

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

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