为什么递归 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 说:
模板参数数量错误(1,应至少为 2(
constexpr std::size_t index_of = std::is_same_v<搜索,第一> ? 0 : 1 + index_of<正在搜索,其他...>;
据我了解,三元运算符需要计算其两个操作数,因此不能在这种类型的递归中使用。
我是对的吗?如果是,为什么会这样?
谢谢。
我将从问题的末尾开始,然后继续工作。
据我了解,三元运算符需要评估其两个操作数
不。三元(意思是"由三个组成"(运算符有三个操作数,而不是两个。计算此运算符时,将计算三个操作数中的两个:条件和条件选取的操作数。
评估不是你的问题所在。
第一个使用三元运算符,不编译。
我想我明白这是为什么了。您正在将条件运算符的结果分配给std::size_t
。为了进行编译,此结果的类型必须std::size_t
或可转换为该类型。因此,编译器需要确定结果的类型。我找到了确定类型的规则。如果第二个或第三个操作数的类型为void
,则第一条规则适用。因此,即使不会评估其中一个操作数,也必须知道它们的两种类型。
好的,那么你的第三个操作数的类型是什么,不会被评估的那个?嗯,1 + index_of<int>
,所以我们最好检查index_of<int>
的声明.糟糕,我们需要两个参数。提示错误消息。
无论如何,这可能是您必须处理的事情,因为在"未找到"的情况下,对于任何一种方法,您都应该得到相同的错误(例如:index_of<unsigned, int, long, float>
(。您可能已经注意到,默认错误消息不能很好地描述出了什么问题,因此您的模板最好专门解决这种情况,即使解决这种情况只是意味着提供更易于理解的编译器错误。
据我了解,三元运算符需要计算其两个操作数,因此不能在这种类型的递归中使用。
不是评估,而是实例化。实例化表达式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
。
- 包含模板文件的递归会导致编译失败
- 使用 SFINAE 作为模板参数的编译时递归
- 为什么我的递归可变参数模板无法编译?
- 递归应用 C++20 范围适配器会导致编译时无限循环
- 我试图用c++编写递归fibonacci序列,但当我编译时,我遇到了一个错误
- 为什么递归 constexpr 模板值无法编译?
- 具有编译问题的简单(递归)可变参数模板"accumulate_for"函数
- 为什么使用递归lambda时会遇到编译错误
- 范围的递归函数(从范围 v3 开始)导致编译发散:为什么
- 如何制作递归编译时间变异结构模板
- 使用C 模板函数编译时间递归
- Boost.Spirit X3 编译时间因递归规则而爆炸
- 提升分配器无法在递归上下文中编译
- Intel C 编译器的编译非常慢,无法编译递归声明返回
- sudoku递归回溯,未提早编译
- 编译时间递归函数以计算两个整数的下一个功能
- 预编译标头是递归的
- 使用选择算法编译时递归排序
- 递归提升::变量类型不能用"-std=c++11 -stdlib=libc++"编译
- 编译递归模板以调用静态成员函数时类型不完整