我正在定义一种方法,该方法使用C ++ 17中的递归模板来知道类型在类型列表中的位置。我尝试了两种方法:一种使用constexpr值,另一种使用constexpr函数。第二个使用if
语句进行编译,而第一个使用三元运算符则不进行编译。
#include <type_traits>
#include <iostream>
template<typename Searching, typename First, typename...Others>
constexpr std::size_t index_of = std::is_same_v<Searching,First> ? 0 : 1 + index_of<Searching,Others...>;
template<typename Searching, typename First, typename...Others>
constexpr std::size_t IndexOf() {
if constexpr(std::is_same_v<Searching,First>)
return 0;
else return 1 + IndexOf<Searching,Others...>();
};
int main() {
std::cout << index_of<int, int> << std::endl; //does not compile
std::cout << IndexOf<int, int>() << std::endl; //compile
//both should return 0
return 0;
}
我的编译器migw64说:
模板参数个数错误(1个,至少应为2个)
constexpr std :: size_t index_of = std :: is_same_v吗? 0:1 + index_of <正在搜索,其他...>;
据我了解,三元运算符需要评估其两个操作数,因此不能在这种类型的递归中使用它。
我是对的吗?如果是的话,为什么会这样?
谢谢。
答案 0 :(得分:1)
我将从问题的结尾开始,然后逐步解决。
据我了解,三元运算符需要评估其两个操作数
不。三元运算符(意思是“由三个组成”)具有三个操作数,而不是两个。在评估此运算符时,将评估三个操作数中的两个:条件和条件选择的操作数。
评估不是您的问题所在。
使用三元运算符的第一个不会编译。
我想我明白为什么会这样。您正在将条件运算符的结果分配给std::size_t
。为了对此进行编译,此结果的类型必须为std::size_t
或可转换为该类型。因此,编译器需要确定结果的类型。我找到了rules for determining the type。如果第二或第三操作数的类型为void
,则适用第一条规则。因此,即使不对这些操作数之一进行求值,也必须知道它们的两个类型。
好,那么您的第三个操作数是什么类型,将不会被求值的那个呢?好吧,它是1 + index_of<int>
,所以我们最好检查一下index_of<int>
的声明。糟糕,我们需要两个参数。提示错误消息。
无论如何,这可能都是您必须处理的事情,因为在“未找到”的情况下,任何一种方法都应该得到相同的错误(例如:index_of<unsigned, int, long, float>
)。您可能已经注意到,默认错误消息不能很好地描述问题所在,因此,即使解决该问题仅意味着提供一个更易于理解的编译器错误,对于模板专门解决该问题也可能是一个好主意。
答案 1 :(得分:1)
据我了解,三元运算符需要评估其两个操作数,因此不能在这种类型的递归中使用它。
不是评估,而是实例化。实例化表达式std::is_same_v<Searching, First> ? 0 : 1 + index_of<Searching, Others...>
时,必须实例化所有三个操作数(不求值),因此由于实例index_of<Searching, Others...>
会发生错误。它类似于if
和if constexpr
之间的区别。如果您以第二种方式将if constexpr
更改为if
,则它也不会编译。
一种解决方法是使用第二种方法(即函数模板)来初始化index_of
,例如
template<typename Searching, typename First, typename... Others>
constexpr std::size_t IndexOf() {
if constexpr(std::is_same_v<Searching,First>)
return 0;
else return 1 + IndexOf<Searching,Others...>();
};
template<typename Searching, typename First, typename...Others>
constexpr std::size_t index_of = IndexOf<Searching, First, Others...>();
或使用模板专门化:
template<typename Searching, typename First, typename... Others>
constexpr std::size_t index_of = 1 + index_of<Searching, Others...>;
template<typename Searching, typename... Others>
constexpr std::size_t index_of<Searching, Searching, Others...> = 0;
如果您想获得更清晰的错误消息,可以将变量模板包装在一个类中并使用static_assert
。