return 本地对象时的 C++11 移动语义
C++11 move semantics when return a local object
我看到了一些类似的问题,但找不到对我的问题的明确解释。这是代码(可以在这里找到:http://melpon.org/wandbox/permlink/nBiik8pMkpKCD3Jv):
#include <iostream>
class A {
public:
explicit A(int a) {std::cout << "Main constructor" << std::endl;}
A(const A& a) {std::cout << "Copy constructor" << std::endl;}
A& operator =(const A& a) {std::cout << "Copy assignment" << std::endl; return *this;}
A(A&& a) {std::cout << "Move constructor" << std::endl;}
A& operator =(A&& a) {std::cout << "Move assignemnt" << std::endl; return *this;}
};
A getA(bool b) {std::cout << "In getA" << std::endl; A a(0); return b ? a : A(1);}
A getA_Move(bool b) {std::cout << "In getA_Move" << std::endl; A a(0); return std::move(b ? a : A(1));}
int main(void) {
std::cout << "\nA a0(getA(true))" << std::endl;
A a0(getA(true));
std::cout << "\nA a1(getA(false))" << std::endl;
A a1(getA(false));
std::cout << "\nA a2(getA(true))" << std::endl;
A a2(getA_Move(true));
std::cout << "\nA a3(getA(false))" << std::endl;
A a3(getA_Move(false));
}
据我了解:函数的 return 是右值(因为与左值相反,它没有名称,因此不能重复使用)。
因此,当创建一个将函数的 return 值作为参数的对象时,如果该参数是可移动的,则应移动该参数。然而,这不是我能观察到的:
Start
A a0(getA(true))
Main constructor
In getA
Copy constructor <- Why is not this a move ?
A a1(getA(false))
Main constructor
In getA
Main constructor
A a2(getA(true))
Main constructor
In getA_Move
Copy constructor <- I would have expected a move here as well
Move constructor
A a3(getA(false))
Main constructor
In getA_Move
Main constructor
Move constructor
0
Finish
谢谢@T.C。我修改了程序以使用正常的 if 然后程序按预期工作。结果可以在这里找到:http://melpon.org/wandbox/permlink/6h0ODi1SHdUj4HvX
这是由于 copy elision 编译器优化。 “复制省略是唯一允许的可以改变可观察到的副作用的优化形式”
简而言之,这是因为 foo ? lvalue-of-X : rvalue-of-X
导致从所选操作数初始化类型为 X
的临时值 - 如果选择第二个则为副本,如果选择第三个则为移动。
然后,该临时文件被 returned - 首先移入 return 值,然后移入 main
中的变量。
"local variables are treated as rvalues in return" 规则仅在所述局部变量被直接 returned 时适用。
GCC 在这里的处理有一点问题——它省略了第三个操作数的移动,即使是 -fno-elide-constructors
。使用 clang,您会在选择第三个操作数时看到 the triple move。
我看到了一些类似的问题,但找不到对我的问题的明确解释。这是代码(可以在这里找到:http://melpon.org/wandbox/permlink/nBiik8pMkpKCD3Jv):
#include <iostream>
class A {
public:
explicit A(int a) {std::cout << "Main constructor" << std::endl;}
A(const A& a) {std::cout << "Copy constructor" << std::endl;}
A& operator =(const A& a) {std::cout << "Copy assignment" << std::endl; return *this;}
A(A&& a) {std::cout << "Move constructor" << std::endl;}
A& operator =(A&& a) {std::cout << "Move assignemnt" << std::endl; return *this;}
};
A getA(bool b) {std::cout << "In getA" << std::endl; A a(0); return b ? a : A(1);}
A getA_Move(bool b) {std::cout << "In getA_Move" << std::endl; A a(0); return std::move(b ? a : A(1));}
int main(void) {
std::cout << "\nA a0(getA(true))" << std::endl;
A a0(getA(true));
std::cout << "\nA a1(getA(false))" << std::endl;
A a1(getA(false));
std::cout << "\nA a2(getA(true))" << std::endl;
A a2(getA_Move(true));
std::cout << "\nA a3(getA(false))" << std::endl;
A a3(getA_Move(false));
}
据我了解:函数的 return 是右值(因为与左值相反,它没有名称,因此不能重复使用)。 因此,当创建一个将函数的 return 值作为参数的对象时,如果该参数是可移动的,则应移动该参数。然而,这不是我能观察到的:
Start
A a0(getA(true))
Main constructor
In getA
Copy constructor <- Why is not this a move ?
A a1(getA(false))
Main constructor
In getA
Main constructor
A a2(getA(true))
Main constructor
In getA_Move
Copy constructor <- I would have expected a move here as well
Move constructor
A a3(getA(false))
Main constructor
In getA_Move
Main constructor
Move constructor
0
Finish
谢谢@T.C。我修改了程序以使用正常的 if 然后程序按预期工作。结果可以在这里找到:http://melpon.org/wandbox/permlink/6h0ODi1SHdUj4HvX
这是由于 copy elision 编译器优化。 “复制省略是唯一允许的可以改变可观察到的副作用的优化形式”
简而言之,这是因为 foo ? lvalue-of-X : rvalue-of-X
导致从所选操作数初始化类型为 X
的临时值 - 如果选择第二个则为副本,如果选择第三个则为移动。
然后,该临时文件被 returned - 首先移入 return 值,然后移入 main
中的变量。
"local variables are treated as rvalues in return" 规则仅在所述局部变量被直接 returned 时适用。
GCC 在这里的处理有一点问题——它省略了第三个操作数的移动,即使是 -fno-elide-constructors
。使用 clang,您会在选择第三个操作数时看到 the triple move。