为什么 decltype(captured_var) 的行为不符合预期?

xml*_*lmx 5 c++ lambda decltype type-deduction c++17

#include <type_traits>

int x = 0;
void f(int const x)
{
    static_assert(std::is_const_v<decltype(x)>); // ok
}

int main() 
{
    int n = 0;
    [n, m = n]
    {
        static_assert(std::is_const_v<decltype(m)>); // ok
        static_assert(std::is_const_v<decltype(n)>); // err
    };
}
Run Code Online (Sandbox Code Playgroud)

在线演示

为什么行为 decltype(captured_var) 不符合预期?

son*_*yao 6

需要注意的是在decltype(n)n指的是局部变量n的定义main(),而不是封闭类型的成员(如你预期)。

[expr.prim.lambda.capture]/11

(强调我的)

一个 lambda 表达式的复合语句中的每个 id 表达式是对复制捕获的实体的 odr 使用,被转换为对闭包类型的相应未命名数据成员的访问。

[注 7:不是 odr-use 的 id 表达式指的是原始实体,而不是闭包类型的成员。但是,这样的 id 表达式仍然会导致实体的隐式捕获。— 尾注]


顺便说一句:Gcc 似乎const直接将数据成员声明为非可变 lambda。这就是为什么它给出const intfor decltype(m)。根据标准,[expr.prim.lambda.capture]/10.2

(强调我的)

如果实体是对对象的引用,则此类数据成员的类型是被引用类型,如果实体是对函数的引用,则是对被引用函数类型的左值引用,否则是对应的捕获实体的类型

我认为 gcc 是错误的;decltype(m)应该导致类型int. 无论如何,decltype(n)确实引用了局部变量n,您可以通过例如将n的类型更改为 来确认这一点int&&

Gcc LIVE
Clang LIVE

  • @Human-Compiler Gcc 似乎直接将不可变 lambda 的数据成员声明为“const”。我检查了标准,它说数据成员的类型应该是相应捕获实体的类型,所以我认为gcc也是错误的。 (3认同)
  • [`clang++`](https://godbolt.org/z/TGcfq8EG4)`表示`n`和`m`都是非`const`。漏洞? (2认同)
  • @songyuanyao如果是这样的话,那是不是意味着 `decltype(&lt;any lambda capture&gt;)` 不应该是 `const`,因为这与 `const` 上下文下的成员变量具有同等性?这是标准中的规范问题吗?现在对我来说,这实际上更像是一个“gcc”错误,因为“decltype(m)”是“const”,偏离了等效的类行为 (2认同)
  • 有趣的是,我们看到不同标准和/或版本的 clang 具有不同的行为。我看到 `m` 和 `n` 都是非常量(就像 Ted 看到的那样),但它们在不可变 lambda 的上下文中也是不可变的。越来越好奇。 (2认同)
  • [MSVC](https://godbolt.org/z/dera64Gf9) 似乎给出与 [clang](https://godbolt.org/z/G3T8e8qTG) 相同的输出(` 中的任一成员都没有 `const` const` 上下文或 lambda 捕获)。[gcc](https://godbolt.org/z/s3nocGTWW) 似乎是唯一将 lambda 捕获视为“const”的一个(至少就 3 个主要编译器供应商而言)。我仍然不太清楚这是否是标准中的歧义,它是否可以是“const”,或者它是否只是 gcc 中的一个错误。 (2认同)