C++14:使用三元表达式从 constexpr 推导出 (auto) return 类型
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");
如果我明确声明 return 类型 T(而不是使用 auto 推断 return 类型),问题就解决了
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.
这是怎么回事?
评估三元表达式的结果类型是 common type of its second and third arguments。
通过让编译器推断出 return 类型,您可以强制它对三元表达式的这两个参数求值。这意味着即使达到终止条件,递归也不会结束,因为当 a==1
时,要找出 fact(0)
的 return 类型,编译器必须继续评估进一步的递归调用fact
,无休止的递归接踵而至。
通过声明return类型,fact(0)
在a==1
时不需要计算,递归可以终止。
对于两个return
语句的情况,相关的标准条款是—
(来自 N4296)§7.1.6.4/9 [dcl.spec.auto]
If a function with a declared return type that contains a placeholder type has multiple return statements, the return type is deduced for each return statement. If the type deduced is not the same in each deduction, the program is ill-formed.
在您的示例中,在对 fact<int>(1)
的调用中,从第一个 return
语句推导出的 return 类型是 int
,因此 return第二个 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>();
}
clang 确实失败并出现错误
fatal error: recursive template instantiation exceeded maximum depth of 256
我正在试验 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");
如果我明确声明 return 类型 T(而不是使用 auto 推断 return 类型),问题就解决了
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.
这是怎么回事?
评估三元表达式的结果类型是 common type of its second and third arguments。
通过让编译器推断出 return 类型,您可以强制它对三元表达式的这两个参数求值。这意味着即使达到终止条件,递归也不会结束,因为当 a==1
时,要找出 fact(0)
的 return 类型,编译器必须继续评估进一步的递归调用fact
,无休止的递归接踵而至。
通过声明return类型,fact(0)
在a==1
时不需要计算,递归可以终止。
对于两个return
语句的情况,相关的标准条款是—
(来自 N4296)§7.1.6.4/9 [dcl.spec.auto]
If a function with a declared return type that contains a placeholder type has multiple return statements, the return type is deduced for each return statement. If the type deduced is not the same in each deduction, the program is ill-formed.
在您的示例中,在对 fact<int>(1)
的调用中,从第一个 return
语句推导出的 return 类型是 int
,因此 return第二个 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>();
}
clang 确实失败并出现错误
fatal error: recursive template instantiation exceeded maximum depth of 256