【问题标题】:Why does decltype(captured_var) not behave as expected?为什么 decltype(captured_var) 的行为不符合预期?
【发布时间】:2021-06-26 13:06:00
【问题描述】:
#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
    };
}

见online demo

为什么 decltype(captured_var) 表现不如预期?

【问题讨论】:

  • 因为如果 lambda 未声明为可变,则所有捕获的变量默认为 const。在这里说m,正如预期的那样是const。

标签: c++ lambda c++17 decltype type-deduction


【解决方案1】:

请注意,在decltype(n) 中,n 指的是在main() 中定义的局部变量n,而不是闭包类型的成员(如您所料)。

[expr.prim.lambda.capture]/11

(强调我的)

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

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


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

(强调我的)

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

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

Gcc LIVE
Clang LIVE

【讨论】:

  • clang++` 表示n 和m 都不是const。错误?
  • @songyuanyao 如果是这样,那是否意味着decltype(&lt;any lambda capture&gt;) 不应该不是 const,因为这将与@987654348 下的成员变量相同@ 语境?这是标准中的规范问题吗?这实际上在我看来更像是一个 gcc 错误,因为 decltype(m) 是 const 偏离了等效的类行为
  • 有趣的是,我们看到不同标准和/或clang版本的不同行为。我看到 m 和 n 都是非常量的(就像 Ted saw 一样),但它们在不可变 lambda 的上下文中也是不可变的。越来越好奇。
  • @Human-Compiler Gcc 似乎将数据成员声明为 const 直接用于非可变 lambda。我查了一下标准,上面说数据成员的类型应该是对应捕获实体的类型,所以我认为gcc也是错误的。
  • MSVC 似乎给出与clang 相同的输出(const 上下文或 lambda 捕获中的任何成员都没有const)。 gcc 似乎是唯一一个将 lambda 捕获视为 const(至少就 3 个主要编译器供应商而言)。我仍然不能完全确定这是否是标准中的模棱两可,它是否可以是const,或者它是否只是gcc中的一个错误。
猜你喜欢
  • 2012-12-07
  • 1970-01-01
  • 1970-01-01
  • 2019-05-09
  • 1970-01-01
  • 1970-01-01
  • 2017-05-17
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多