【问题标题】:Type of a value captured in a lambda from a reference type, not using generalized capture从引用类型在 lambda 中捕获的值的类型,不使用广义捕获
【发布时间】:2018-12-16 14:59:54
【问题描述】:

当我编译以下程序时(https://wandbox.org/permlink/fl6yrLYI2sRrZJHP

#include <iostream>

using std::cout;
using std::endl;

template <typename...> class WhichType;

int main() {
    const auto& integer = 0;
    [integer]() {
        WhichType<decltype(integer)>{};
    }();
}

为什么编译器会说decltype(integer)const int&amp;?我的理解是这应该是const intdecltype((integer)) 应该已经解析为const int&amp;

换句话说,我的理解是 lambda 会解析为这样的结构

struct Lambda {
public:
  explicit Lambda(const int& integer_) : integer{integer_} {}
  void operator()() const {
    WhichType<decltype(integer)>{};
  }

  const int integer;
};

为什么decltype(integer) 解析为引用类型?这只是decltype 表现特殊的另一种情况吗?类似于结构化绑定中引用类型和实际类型的区别?

【问题讨论】:

  • Visual C++ 2017 报告const int。 MinGW g++ 7.3.0 报告const int&amp;.

标签: c++ lambda language-lawyer c++17 decltype


【解决方案1】:

这种行为似乎是标准规定的。它似乎是 lambda 中 decltype() 的一个特定例外:

8.1.5.2 Captures               [expr.prim.lambda.capture]

...

14 Every occurrence of decltype((x)) where x is a possibly parenthesized id-
expression that names an entity of automatic storage duration is treated as if x
were transformed into an access to a corresponding data member of the closure
type that would have been declared if x were an odr-use of the denoted entity.

Example:

void f3() {
   float x, &r = x;
   [=] { // x and r are not captured (appearance in a decltype operand is not an odr-use)
      decltype(x) y1; // y1 has type float
      decltype((x)) y2 = y1; // y2 has type float const& because this lambda is
                             // not mutable and x is an lvalue

      decltype(r) r1 = y1;  // r1 has type float& (transformation not considered)
      decltype((r)) r2 = y2; // r2 has type float const&
 };
}
— end example ]

那里的关键词似乎是“未考虑转型”。

【讨论】:

    【解决方案2】:

    这里有两个相关的规则。

    在 lambda 中引用某些东西是什么意思?那是expr.lambda.prim.capture,强调我的:

    lambda-expressioncompound-statement 中的每个 id-expression 是一种 odr-use 被复制捕获的实体的数据被转换为对闭包类型的相应未命名数据成员的访问。

    那么,decltype 是什么意思?那是[dcl.type.simple]:

    对于表达式edecltype(e)表示的类型定义如下: - [...]
    - 否则,如果e 是未加括号的id-expression 或未加括号的类成员访问,则decltype(e)e 命名的实体的类型。如果没有这样的实体,或者如果e 命名了一组重载函数,则程序是非良构的;

    decltype(integer) 不是 integer 的 odr-use,所以这只是 integer 的类型,即 const int&amp;。这实际上是decltype 最正常的含义 - 它只是变量的类型(它只是在您可能认为它是发明捕获的类型的上下文中 - 尽管在这种情况下,发明捕获的类型只是int,不是const int)。

    你出错的地方是你如何为 lambda 命名合成类型的成员变量。更准确的是:

    int main() {
        const auto& integer = 0;
        struct __lambda {
            int __integer;
    
            auto operator() const {
                WhichType<decltype(integer)>{}; // NB: still integer, not __integer, because not an odr-use
            };
        }
    
        __lambda{integer}();
    }
    

    有一个专门针对 decltype((x)) 的单独规则,在这种情况下,decltype((integer)) 实际上仍然是 const int&amp;,但出于不同的原因,但该规则是 CWG 1913 的主题,并且该措辞一直是从最新的工作草案中删除。

    【讨论】:

      猜你喜欢
      • 2017-03-24
      • 1970-01-01
      • 1970-01-01
      • 2014-08-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-02-04
      相关资源
      最近更新 更多