使用 constexpr + auto 作为返回和参数类型的奇怪类型推导

Edw*_*ard 0 c++ g++ compiler-optimization c++20

我一直在玩编译器优化和编译器资源管理器,并注意到 g++ 9.3(本地测试)中的以下缺点。该问题似乎在 g++ 10.1(在编译器资源管理器上测试)中仍然存在。我正在使用

请注意以下代码:

#include <iostream>
#include <iomanip>

constexpr auto fib( auto x )
{
    if( x == 0 )
        return 0;
    else if( x == 1 )
        return 1;
    else
        return fib( x - 1 ) + fib( x - 2 );
}

int main( int argc, char * argv[] )
{
    std::cerr << std::setprecision(10) << fib( 47.l );
}
Run Code Online (Sandbox Code Playgroud)

编译器资源管理器链接在这里

我知道如果我放了 47,模板参数推导会推导出 function int foo( int x ),但是即使我传递一个 long double 文字,这种情况仍然存在。

这会导致溢出。

为什么编译器不能在编译时推断出我的返回类型应该是双精度型?我会期望,因为 fib 被标记为 constexpr,并且我正在使用 -O3 进行编译,即使我传递了一个整数,g++ 也能够通过意识到 fib 是指数的来推断出需要一个 double。

即使上述内容非常困难,为什么传入 long double 文字不能解决问题?我希望该函数能够实现该函数的第三个分支必须返回一个 long double,因此返回类型应该是一个 long double。

编译器仅在 fib 更改为返回 0.l 和 1.l 时才意识到需要 long double ,如下所示:

constexpr auto fib( auto x )
{
    if( x == 0 )
        return 0.l;
    else if( x == 1 )
        return 1.l;
    else
        return fib( x - 1 ) + fib( x - 2 );
}
Run Code Online (Sandbox Code Playgroud)

有趣的是,仅将其中一个返回值更改为 long double 文字,如下所示:

    if( x == 0 )
        return 0.l;
    else if( x == 1 )
        return 1;
Run Code Online (Sandbox Code Playgroud)

导致以下错误:

error: inconsistent deduction for auto return type: ‘long double’ and then ‘int’
Run Code Online (Sandbox Code Playgroud)

这怎么会抛出错误,但第一个例子不会?

cig*_*ien 7

当您定义这样的函数时:

constexpr auto f(auto x) 
{
  return 42;
}
Run Code Online (Sandbox Code Playgroud)

编译器别无选择,只能将返回类型推导出为int,因为这是文字的类型42。如果您f使用其他类型的参数调用并不重要:

f(42.l);  
Run Code Online (Sandbox Code Playgroud)

返回类型仍然是int,尽管 的类型xlong double

但是,您可以明确要求返回类型与参数类型相同:

constexpr auto f(auto x) -> decltype(x)
{
  return 42;
}
Run Code Online (Sandbox Code Playgroud)

现在返回值将被转换为f被调用的参数的类型。