我正在编写一个模板,该模板定义在类型参数包中赋予它的类型,并且其位置与传递给模板的数字相同。这是我写的:
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>(), "");