为什么递归 constexpr 模板值不编译?
Why recursive constexpr template value does not compile?
我正在使用 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 说:
wrong number of template arguments (1, should be at least 2)
constexpr std::size_t index_of = std::is_same_v<Searching,First> ? 0 : 1 + index_of<Searching,Others...>;
据我了解,三元运算符需要计算它的两个操作数,因此不能用于此类递归。
我说得对吗?如果是,为什么会这样?
谢谢。
我将从问题的末尾开始,然后继续。
From what I understand, the ternary operator needs to evaluate its two operands
没有。三元(意思是 "made of three")运算符有三个操作数,而不是两个。评估此运算符时,将评估三个操作数中的两个:条件和条件选择的操作数。
评价不是你的问题所在
the first, using ternary operator, does not compile.
我想我明白这是为什么了。您正在将条件运算符的结果分配给 std::size_t
。为了编译,此结果的类型必须是 std::size_t
或可转换为该类型。所以编译器需要判断结果的类型。我找到了rules for determining the type。如果第二个或第三个操作数的类型为 void
,则适用第一条规则。因此,即使不会计算其中一个操作数,它们的 类型 也必须是已知的。
好的,那么你的第三个操作数的类型是什么,它不会被评估?好吧,它是 1 + index_of<int>
,所以我们最好检查一下 index_of<int>
的声明。糟糕,我们需要两个参数。提示错误消息。
这可能是您无论如何都必须处理的事情,因为在 "not found" 情况下(例如:index_of<unsigned, int, long, float>
),您应该使用任何一种方法得到相同的错误。您可能已经注意到,默认错误消息并不能很好地描述出错的地方,因此您的模板专门解决这种情况可能是个好主意,即使解决这种情况只是意味着提供更易于理解的编译器错误。
From what I understand, the ternary operator needs to evaluate its two operands, so it can not be used in this type of recursion.
不是求值,是实例化。实例化表达式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;
如果你想要更清晰的错误信息,你可以将变量模板包裹在class中并使用static_assert
.
我正在使用 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 说:
wrong number of template arguments (1, should be at least 2)
constexpr std::size_t index_of = std::is_same_v<Searching,First> ? 0 : 1 + index_of<Searching,Others...>;
据我了解,三元运算符需要计算它的两个操作数,因此不能用于此类递归。
我说得对吗?如果是,为什么会这样?
谢谢。
我将从问题的末尾开始,然后继续。
From what I understand, the ternary operator needs to evaluate its two operands
没有。三元(意思是 "made of three")运算符有三个操作数,而不是两个。评估此运算符时,将评估三个操作数中的两个:条件和条件选择的操作数。
评价不是你的问题所在
the first, using ternary operator, does not compile.
我想我明白这是为什么了。您正在将条件运算符的结果分配给 std::size_t
。为了编译,此结果的类型必须是 std::size_t
或可转换为该类型。所以编译器需要判断结果的类型。我找到了rules for determining the type。如果第二个或第三个操作数的类型为 void
,则适用第一条规则。因此,即使不会计算其中一个操作数,它们的 类型 也必须是已知的。
好的,那么你的第三个操作数的类型是什么,它不会被评估?好吧,它是 1 + index_of<int>
,所以我们最好检查一下 index_of<int>
的声明。糟糕,我们需要两个参数。提示错误消息。
这可能是您无论如何都必须处理的事情,因为在 "not found" 情况下(例如:index_of<unsigned, int, long, float>
),您应该使用任何一种方法得到相同的错误。您可能已经注意到,默认错误消息并不能很好地描述出错的地方,因此您的模板专门解决这种情况可能是个好主意,即使解决这种情况只是意味着提供更易于理解的编译器错误。
From what I understand, the ternary operator needs to evaluate its two operands, so it can not be used in this type of recursion.
不是求值,是实例化。实例化表达式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;
如果你想要更清晰的错误信息,你可以将变量模板包裹在class中并使用static_assert
.