当右值引用被副本捕获时会发生什么?
What happens when an rvalue reference is captured by copy?
如果我有
std::function<int()> &&x = [] { return 1; };
auto lambda = [y = x(), x] {
return y * x();
};
lambda
中 x
的类型是什么: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()> &
).
如果我有
std::function<int()> &&x = [] { return 1; };
auto lambda = [y = x(), x] {
return y * x();
};
lambda
中 x
的类型是什么: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
.
所有三个编译器都同意第二个断言。由于所有三个编译器都在 sizeof(void*) > sizeof(int)
的平台上进行了测试,这意味着它们都复制了变量而不是存储引用。
What is the type of
x
insidelambda
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()> &
).