C++14:使用三元表达式从 constexpr 推导(自动)返回类型
C++14: deduced (auto) return types from constexpr with ternary expressions
我正在C++14中试验constexpr函数。以下计算阶乘的代码按预期工作:
template <typename T>
constexpr auto fact(T a) {
if(a==1)
return 1;
return a*fact(a-1);
}
int main(void) {
static_assert(fact(3)==6, "fact doesn't work");
}
当它使用 clang 编译如下时:
> clang++ --version
clang version 3.5.0 (tags/RELEASE_350/final)
Target: x86_64-unknown-linux-gnu
Thread model: posix
> clang++ -std=c++14 constexpr.cpp
但是,当我更改fact
定义以使用三元?
运算符时:
template <typename T>
constexpr auto fact(T a) {
return a==1 ? 1 : a*fact(a-1);
}
我收到以下编译器错误:
> clang++ -std=c++14 constexpr.cpp
constexpr.cpp:12:31: fatal error: recursive template instantiation exceeded maximum depth of
256
return a==T(1) ? T(1) : a*fact(a-1);
... snip ...
constexpr.cpp:16:19: note: in instantiation of function template specialization 'fact<int>'
requested here
static_assert(fact(3)==6, "fact doesn't work");
如果我显式声明返回类型 T(而不是使用 auto 来推断返回类型),则问题已修复
template <typename T>
constexpr T fact(T a) {
return a==1 ? 1 : a*fact(a-1);
}
如果我删除模板参数,则重复模式(三元版本失败,if
版本有效)
// this works just fine
constexpr auto fact(int a) {
if(a==1)
return 1;
return a*fact(a-1);
}
而这失败了
constexpr auto fact(int a) {
return a==1 ? 1 : a*fact(a-1);
}
出现以下错误
> clang++ -std=c++14 constexpr.cpp
constexpr.cpp:16:25: error: function 'fact' with deduced return type cannot be used before it
is defined
return a==1 ? 1 : a*fact(a-1);
^
constexpr.cpp:15:16: note: 'fact' declared here
constexpr auto fact(int a) {
^
constexpr.cpp:20:26: error: invalid operands to binary expression ('void' and 'int')
static_assert(fact(3)==6, "fact doesn't work");
~~~~~~~^ ~
2 errors generated.
这是怎么回事?
计算三元表达式的结果类型是其第二个和第三个参数的通用类型。
通过让编译器推导返回类型,可以强制它计算三元表达式的这两个参数。这意味着即使达到终止条件,递归也不会结束,因为当a==1
时,要找出fact(0)
的返回类型,编译器必须继续计算对fact
的进一步递归调用,并且随之而来的是无休止的递归。
通过声明返回类型,fact(0)
在a==1
时不需要求值,并且递归能够终止。
至于两份return
声明的情况,相关的标准条款是——
(来自 N4296) §7.1.6.4/9[dcl.spec.auto]
如果具有包含占位符类型的声明返回类型的函数具有多个 return 语句,则为每个 return 语句推导返回类型。如果推导的类型在每个推导中都不相同,则程序格式不正确。
在您的示例中,在对 fact<int>(1)
的调用中,从第一个 return
语句推导出的返回类型是 int
,因此第二个 return
语句中 fact<int>(0)
的返回类型也只能是int
。这意味着编译器不需要计算fact<int>(0)
主体,递归可以终止。
实际上,如果您在第二个return
语句中也强制计算对fact
调用的调用,例如通过更改第一个示例,使T
是非类型模板参数
template <unsigned T>
constexpr auto fact() {
if(T==1)
return 1;
return T*fact<T-1>();
}
叮当失败并显示错误
致命错误:递归模板实例化超出了最大深度 256
现场演示
- 具有默认模板参数的多态类的模板推导失败
- lambda参数转换为constexpr技巧,然后获取带链接的数组
- 模板-模板参数推导:三个不同的编译器三种不同的行为
- 多成员Constexpr结构初始化
- 视图中的参数推导失败:take_while
- 如何在C++20中创建模板别名的推导指南
- 条件constexpr函数
- 同时具有"聚合初始化"和"模板推导"
- 当函数模板参数是具有默认参数的类模板时,函数模板参数的推导如何执行
- constexpr 函数中的非文字(通过 std::is_constant_evaluated)
- Visual C++ constexpr Hints
- 如何确认我的constexpr表达式实际上已经在编译时执行
- 具有默认模板类型的默认构造函数的类型推导
- 为什么constexpr的性能比正常表达式差
- 是否可以使用if constexpr删除控制流语句
- 使用constexpr + auto作为返回和参数类型的奇怪类型推导
- 模板推导指南可以调用constexpr函数吗
- Constexpr类模板成员函数是否具有推导的void返回类型
- C++14:使用三元表达式从 constexpr 推导(自动)返回类型
- 什么是 constexpr 的推导类型