【问题标题】:Weird type deduction using constexpr + auto as return and parameter type使用 constexpr + auto 作为返回和参数类型的奇怪类型推导
【发布时间】:2020-09-13 10:10:11
【问题描述】:

我一直在使用编译器优化和编译器资源管理器,并注意到 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 );
}

编译器资源管理器链接here

我知道如果我输入 47,模板参数推导会推导出函数 int foo( int x ),但即使我传递了一个长的双精度字面值,这种情况仍然存在。

这会导致溢出。

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

即使上面的内容非常困难,为什么传入一个长的双精度字面量也不能解决问题?我希望函数意识到函数的第三个分支必须返回一个 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 );
}

有趣的是,只将其中一个返回值更改为长双精度字面值,如下所示:

    if( x == 0 )
        return 0.l;
    else if( x == 1 )
        return 1;

导致以下错误:

error: inconsistent deduction for auto return type: ‘long double’ and then ‘int’

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

【问题讨论】:

  • 推断返回类型取决于函数中的返回语句,而不取决于传入的参数类型。
  • 仅仅因为你的函数参数是双精度数并不意味着函数会返回双精度数。函数返回的唯一值不是0就是1,所以一定是int,这和返回int和int相加的结果就是int是一致的。因此,无论其参数是什么,该函数都会返回一个 int。
  • "g++ 将能够通过意识到 fib 是指数来推断出需要双精度数" - 这不是 auto(或模板类型推断)的工作方式。您指定类型(有时是间接的),编译器会应用它们。不允许根据它认为你可能会如何使用一个函数来任意选择。
  • 我会喜欢心灵感应编译器。做我想做的而不是我告诉他们做的程序将使我的生活更轻松。但我必须从我的播放列表中删除终结者配乐。这可能会很糟糕。
  • 编译器资源管理器告诉我函数签名是 int fib int。我现在明白为什么返回类型是int,但是为什么参数类型也是int?

标签: c++ g++ compiler-optimization c++20


【解决方案1】:

当你定义这样的函数时:

constexpr auto f(auto x) 
{
  return 42;
}

编译器别无选择只能将返回类型推断为int,因为这是文字42 的类型。如果您使用其他类型的参数调用 f 也没关系:

f(42.l);  

返回类型仍然是int,虽然x的类型是long double

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

constexpr auto f(auto x) -> decltype(x)
{
  return 42;
}

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多