在lambda中从引用类型捕获的值的类型,而不使用广义捕获

时间:2018-07-09 00:03:47

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

当我编译以下程序(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&?我的理解是,这应该是const int,而decltype((integer))应该已经解析为const int&

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

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

  const int integer;
};

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

2 个答案:

答案 0 :(得分:1)

此行为似乎是标准规定的。似乎是decltype()在lambda中的特定例外:

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 ]

那里的关键词似乎是“不考虑转变”。

答案 1 :(得分:1)

这里有两个相关规则。

在lambda中引用某些东西意味着什么?在expr.lambda.prim.capture中,请强调:

  在 lambda表达式 compound-statement 中的每个 id-expression ,被复制捕获的实体的>转换为对闭包类型的相应未命名数据成员的访问。

然后decltype是什么意思?在[dcl.type.simple]中:

  

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

decltype(integer)不是integer的奇特用法,因此它只是integer的类型,即const int&。实际上,这是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&,但出于不同的原因,但是该规则是{{ 3}},该措辞已从最新的工作草案中删除。