通过复制和decltype

时间:2015-10-01 18:48:49

标签: c++ c++11 lambda

考虑一下简单的程序:

int i = 0;
int& j = i;

auto lambda = [=]{
    std::cout << &j << std::endl; //odr-use j
};

根据[expr.prim.lambda],闭包成员变量j应该有int类型:

  

如果隐式捕获并且 capture-default =或者使用捕获显式捕获实体,则捕获实体 不是&amp;的形式标识符&amp;标识符初始值设定项。对于由副本捕获的每个实体,在闭包类型中声明未命名的非静态数据成员。这些成员的声明顺序未指定。 此类数据成员的类型是相应捕获实体的类型(如果实体不是对象的引用,或引用的类型

所以我要打印的是与外部作用域inti无关的某些j的地址。这一切都很好。但是,当我扔进decltype

auto lambda = [j] {
    std::cout << &j << std::endl;
    static_assert(std::is_same<decltype(j), int>::value, "!"); // error: !
};

无法编译,因为decltype(j)评估为int&。为什么?如果不是,那个范围内的j应该引用数据成员吗?

作为相关的后续操作,如果lambda捕获是带有[j=j]{...} init-capture ,那么然后 clang会将decltype(j)报告为{ {1}}而非int。为什么不同?

1 个答案:

答案 0 :(得分:9)

名称查找在lambda表达式中的工作方式有点奇怪: id-expressions 引用由copy 捕获的实体从对捕获实体的访问转换为访问闭包类型的存储数据成员 - 但仅当这些访问构成 odr-uses 时。请注意,由于隐式捕获,如果没有使用odr,则可能没有此类数据成员。

decltype不构成使用odr,因此它将始终引用捕获的实体(原始),而不是数据成员(副本)。

C ++ 11 [expr.prim.lamba] p17

  

每个id-expression都是由捕获的实体的 odr-use   copy被转换为对相应未命名数据的访问   闭包类型的成员。

此外,p18甚至在一个例子中显示了这种奇怪的效果:

  

decltype((x))的每次出现x都可能带有括号   命名自动存储持续时间的实体的 id-expression 是   处理好像x被转换为对相应的访问权限   如果x将声明的闭包类型的数据成员   是对所表示的实体的一种使用。 [示例:

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&
    };
}
     

- 结束示例]

C ++ 14 init-captures 也被认为是以副本捕获,因为C ++ 14 [expr.prim.lambda] p15

  

如果隐式捕获实体,则捕获实体    capture-default =,或者是否通过捕获明确捕获   该格式不是& 标识符& 标识符初始值设定项

然而,正如T.C.指出的那样,它们并没有捕获它们已被初始化的实体,而是捕获了一个虚拟变量&#34;它也用于类型推导[expr.prim.lambda] p11

  

init-capture 的行为就像声明并明确捕获a一样   形式为“auto init-capture ;”的变量,其声明区域为    lambda-expression 的复合语句[...]

类型推导改变了这个变量的类型,例如: char const[N] - &gt; char const*,原始实体甚至可能没有类型,例如[i = {1,2,3}]{}

因此,lambda j中的 id-expression [j=j]{ decltype(j) x; }引用此虚拟变量,其类型为int,而不是int&