当右值引用被副本捕获时会发生什么?

What happens when an rvalue reference is captured by copy?

如果我有

std::function<int()> &&x = [] { return 1; };
auto lambda = [y = x(), x] { 
    return y * x();
};

lambdax 的类型是什么:std::function<int()>std::function<int()>&std::function<int()>&&?如果是第一个,捕获的 x 是由移动构造函数初始化的,还是由复制构造函数初始化的(我需要 [y = x(), x = std::move(x)] 来移动它)?

我无法向律师解释这一点,但 here 是一些 playground 代码。看起来 clang 和 gcc 假定一个右值引用,而 msvc 假定一个没有引用限定符的值。

但是,在所有情况下,您的对象都是复制构造的。

x 使用复制构造函数按值捕获。

x 的类型”有点没有实际意义。 lambda class 的 class 成员具有类型 std::function<int()>,因此不是引用。表达式 x 的类型为 const std::function<int()>,并且是一个左值。 (它是 const 因为我们在 const 限定的 operator() 中。它不是引用,因为表达式与变量不同,can't have reference types。)

示例:

#include <type_traits>

int main()
{
    int &&x = 42;
    auto lambda = [x]
    {
        static_assert(std::is_same<decltype((x)), const int&>::value, "");
    };

    static_assert(sizeof lambda == sizeof(int), "");
}

Clang & MSVC 同意第一个断言,而 GCC 认为类型是 int &。我认为这是 decltype.

中的 GCC 错误

所有三个编译器都同意第二个断言。由于所有三个编译器都在 sizeof(void*) > sizeof(int) 的平台上进行了测试,这意味着它们都复制了变量而不是存储引用。

What is the type of x inside lambda

x 是通过复制捕获的,闭包类型中声明的数据成员类型将是 std::function<int()>[expr.prim.lambda.capture]/10

(强调我的)

For each entity captured by copy, an unnamed non-static data member is declared in the closure type. ... The type of such a data member is the referenced type if the entity is a reference to an object, ...

is the captured x initialized by the move constructor, or by the copy one (and I need [y = x(), x = std::move(x)] to move it instead)?

是的,你必须显式地写 x = std::move(x),否则 x 将被复制构造,因为捕获的对象 x 本身是一个左值。

以及为什么decltype(x)会导致类型std::function<int()>&&,因为这里的x指的是定义在lambda之前的局部变量x[expr.prim.lambda.capture]/11

(强调我的)

Every id-expression within the compound-statement of a lambda-expression that is an odr-use of an entity captured by copy is transformed into an access to the corresponding unnamed data member of the closure type.

[Note 7: An id-expression that is not an odr-use refers to the original entity, never to a member of the closure type. However, such an id-expression can still cause the implicit capture of the entity. — end note]

BTW (x) 不是 id-expression 而这里的 x 指的是闭包类型的成员。然后 decltype((x)) 导致类型 const std::function<int()> & (因为 (x) 是左值表达式并且 lambda 的 operator() 是 const 限定的;对于 mutable lambda 它将是 std::function<int()> &).