C++14:使用三元表达式从 constexpr 推导出(自动)返回类型

标签 c++ ternary-operator c++14 constexpr

我正在 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.

这是怎么回事?

最佳答案

计算三元表达式的结果类型是 common type of its second and third arguments .

通过让编译器推断返回类型,您可以强制它将这两个参数都计算为三元表达式。这意味着即使达到终止条件,递归也不会结束,因为当 a==1 , 找出 fact(0) 的返回类型编译器必须继续评估对 fact 的进一步递归调用。 ,然后无限递归。

通过说明返回类型,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 推导出的返回类型声明是 int ,所以 fact<int>(0) 的返回类型在第二个 return语句只能是 int也是。这意味着编译器不需要评估 fact<int>(0) 的主体。并且递归可以终止。

确实,如果您强制评估对 fact 的调用在第二个 return语句也是如此,例如通过更改第一个示例,使 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

Live demo

关于C++14:使用三元表达式从 constexpr 推导出(自动)返回类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/27814859/

相关文章:

C++ Lambda 没有 operator()

c++ - QString 到 char* 的转换

c++ - 在其静态递归函数中展开struct类型模板参数包

c++ - 将成员函数指针转换为字符串

reactjs - 在 JSX 语法(React)中使用三元条件表达式时出现语法错误

swift - 如何在三元条件运算符中使用 Optional 变量?

c++ - std::map::iterator 递减单个元素

Java 三元运算符和 NullPointerException

c++ - 为什么 auto 不能用于重载函数?

c++ - 将 C++ 声明标记为已弃用的可移植方法,将被 C++ 11 接受