可以捕获的实体的 decltype:它应该产生 lambda 之外的实体类型吗?

问题描述 投票:0回答:1

考虑这个简单的独立代码:

template<typename T>
void foo();

void bar() {
    int i;
    auto l = [&i]() -> decltype(auto) {
        decltype(auto) x = i;
        foo<decltype(x)>();
        foo<decltype(i)>();
        return static_cast<decltype(i)>(i);
    };
    l();
    foo<decltype(l())>();
}

GCC 生成以下内容:

bar():
        sub     rsp, 8
        call    void foo<int&>()
        call    void foo<int>()
        add     rsp, 8
        jmp     void foo<int>()

Clang 生成以下内容:

bar():                                # @bar()
        push    rax
        call    void foo<int>()
        call    void foo<int>()
        pop     rax
        jmp     void foo<int>()                     # TAILCALL

MSVC 生成以下内容:

void bar(void) PROC                                        ; bar, COMDAT
$LN8:
        sub     rsp, 40                             ; 00000028H
        call    void foo<int &>(void)                  ; foo<int &>
        call    void foo<int &>(void)                  ; foo<int &>
        add     rsp, 40                             ; 00000028H
        jmp     void foo<int &>(void)                        ; foo<int &>
void bar(void) ENDP   

 

似乎三个编译器都不同意。哪一个是正确的,C++ 标准的哪一部分证实了它?

在我看来,

decltype(i)
应该永远是
int
,而不是
int&
,无论是否被捕获。

强制编译器资源管理器链接

c++ lambda language-lawyer decltype-auto
1个回答
0
投票

decltype(auto)
[dcl.type.auto.deduct]/4:

中指定

如果 占位符类型说明符 的形式为 type-constraintopt

decltype(auto)
,则
T
应单独作为占位符。
T
推导的类型按照 [dcl.type.decltype] 中的描述确定,就好像 E
decltype
的操作数一样。

这意味着

decltype(auto) x = i
相当于
decltype(i) x = i
。关于
decltype(i)
[dcl.type.decltype]/1.3 指出:

  • 否则,如果 E 是不带括号的 id-expression 或不带括号的类成员访问 ([expr.ref]),则
    decltype(E)
    是由 E 命名的实体的类型。如果没有这样的实体,则程序是格式错误的;

因此,

decltype(i)
是由
i
命名的实体类型,即
int
。因此,
x
属于
int
类型,因此,
decltype(x)
也是
int
。因此,Clang 在这种情况下是正确的。 GCC 中的这个错误已在 GCC 14 中修复(Patrick)。


但是,此修复似乎会导致 GCC 中的另一个错误。如果我们将

i
的类型更改为
int&
,则以下代码演示了此问题 (Godbolt):

template <typename T> void foo();
void bar(int &i) {
  auto l = [&i]() -> decltype(auto) {
    decltype(auto) x = i;
    foo<decltype(x)>(); // [Clang] int& [GCC 14] int  [GCC 13] int&
    foo<decltype(i)>(); // [Clang] int& [GCC 14] int& [GCC 13] int&
    return i;
  };
  l();
  foo<decltype(l())>(); // [Clang] int& [GCC 14] int  [GCC 13] int&
}

按照与上面相同的推理,Clang 和 GCC 13 在这种情况下是正确的。

© www.soinside.com 2019 - 2024. All rights reserved.