为什么以下代码不会导致移动对象而不是复制对象?
Why does the following code not result in moving the object instead of copying?
#include <iostream>
#include <time.h>
class A
{
public:
A() { std::cout << "a ctor\n"; }
A(const A&) { std::cout << "a copy ctor\n"; }
A(A&&) { std::cout << "a move ctor\n"; }
};
A f(int i)
{
A a1;
return i != 0 ? a1 : A{};
}
int main()
{
srand(time(0));
f(rand());
return 0;
}
输出为:
a ctor
a copy ctor
我希望 f() 中的 a1 将被移动而不是复制。
如果我稍微改变 f() ,它不再是副本而是移动:
A f(int i)
{
A a1;
if (i != 0)
{
return a1;
}
return A{};
}
输出为:
a ctor
a move ctor
你能解释一下这是如何工作的吗?
海湾合作委员会 9.3.0
(编辑:添加随机数以防止 RVO)
您看到的差异是由于使用了 ternary/conditional 运算符。三元运算符为其第二个和第三个操作数确定 公共类型 和 值类别 ,这是在编译时确定的。参见 here。
你的情况:
return i != 0 ? a1 : A{};
公共类型是 A
,公共值类别是纯右值,因为 A{}
是纯右值。但是,a1
是一个左值,在左值到右值的转换中必须制作它的纯右值临时副本。这解释了为什么您会看到在条件为真时调用复制构造函数:创建 a1
的副本以将其转换为纯右值。纯右值被您的编译器复制。
在第二个示例中,您有一个 if
语句,这些规则不适用于三元运算符的情况。所以这里没有调用复制构造函数。
为了解决您对第二个和第三个操作数具有左值的条件语句的评论,根据复制省略的rules,如果:
In a return statement, when the operand is the name of a non-volatile
object with automatic storage duration, which isn't a function
parameter or a catch clause parameter, and which is of the same class
type (ignoring cv-qualification) as the function return type. This
variant of copy elision is known as NRVO, "named return value
optimization".
像
这样的条件语句
return i != 0 ? a1 : a1;
其中第二个和第三个操作数是左值,不满足此条件。该表达式是一个条件,而不是一个自动对象的名称。因此没有复制省略和复制构造函数被调用。
#include <iostream>
#include <time.h>
class A
{
public:
A() { std::cout << "a ctor\n"; }
A(const A&) { std::cout << "a copy ctor\n"; }
A(A&&) { std::cout << "a move ctor\n"; }
};
A f(int i)
{
A a1;
return i != 0 ? a1 : A{};
}
int main()
{
srand(time(0));
f(rand());
return 0;
}
输出为:
a ctor
a copy ctor
我希望 f() 中的 a1 将被移动而不是复制。 如果我稍微改变 f() ,它不再是副本而是移动:
A f(int i)
{
A a1;
if (i != 0)
{
return a1;
}
return A{};
}
输出为:
a ctor
a move ctor
你能解释一下这是如何工作的吗? 海湾合作委员会 9.3.0
(编辑:添加随机数以防止 RVO)
您看到的差异是由于使用了 ternary/conditional 运算符。三元运算符为其第二个和第三个操作数确定 公共类型 和 值类别 ,这是在编译时确定的。参见 here。
你的情况:
return i != 0 ? a1 : A{};
公共类型是 A
,公共值类别是纯右值,因为 A{}
是纯右值。但是,a1
是一个左值,在左值到右值的转换中必须制作它的纯右值临时副本。这解释了为什么您会看到在条件为真时调用复制构造函数:创建 a1
的副本以将其转换为纯右值。纯右值被您的编译器复制。
在第二个示例中,您有一个 if
语句,这些规则不适用于三元运算符的情况。所以这里没有调用复制构造函数。
为了解决您对第二个和第三个操作数具有左值的条件语句的评论,根据复制省略的rules,如果:
In a return statement, when the operand is the name of a non-volatile object with automatic storage duration, which isn't a function parameter or a catch clause parameter, and which is of the same class type (ignoring cv-qualification) as the function return type. This variant of copy elision is known as NRVO, "named return value optimization".
像
这样的条件语句return i != 0 ? a1 : a1;
其中第二个和第三个操作数是左值,不满足此条件。该表达式是一个条件,而不是一个自动对象的名称。因此没有复制省略和复制构造函数被调用。