考虑这个有三个构造函数的类:
class Circle {
public:
Circle(int r) {
_radius = r;
}
Circle(const Circle& c){
_radius = c.radius();
cout << endl << "Copy constructor with lvalue reference. Radius: " << _radius;
}
Circle(Circle&& c){
_radius = c.radius();
cout << endl << "Copy constructor with rvalue reference. Radius:" << _radius;
}
int radius() const {
return _radius;
}
private:
int _radius;
};
int main() {
Circle c1(2);
Circle c2(c1);
cout << endl << c2.radius();
Circle c3(Circle(4));
cout << endl << c3.radius();
return 0;
}
编译为“g ++ -std = c ++ 0x”。输出是:
Copy constructor with lvalue reference. Radius: 2
2
4
行。调用前两种情况的正确构造函数。但对于 第三种情况,即Circle c3(Circle(4)),我期待第三种构造函数, (要用rvalue referecne复制构造函数)要调用,但事实并非如此。 显然有一些构造函数被调用,因为c3被正确实例化了但是我 不明白为什么编译器没有使用显式提供的 一。我在这里错过了什么吗?
答案 0 :(得分:9)
没有调用move构造函数,因为你的编译器对你的代码来说太聪明了;)
Circle c1(2);
这只是使用int
转换构造函数构建一个对象。
Circle c2(c1);
这是复制操作。 c1
是一个l值,因此它会引发一个副本。
Circle c3(Circle(4));
在这里,您的编译器会认识到您基本上是在告诉它构造对象两次。所以 elides 是其中一个对象构造函数。在这种情况下,C ++规范允许这样做。
如果你的编译器无法完成构造,那么它就会执行一次移动。此外,如果你的编译器不能那个,那就扔掉它。
所以没有继续前进。
答案 1 :(得分:5)
为了获取rvalue引用,它应该是非const的,因为构造函数参数的内容将被移动,并且通常这是一个改变操作数状态的操作(尽管不是在你的特定情况下):
Circle(Circle&& c){ }
另外,你在这里看到了一个副本:
Circle c3(Circle(4));
所以移动构造函数不会被调用。这是可能会或可能不会发生的标准编译器优化。如果你要像这样构建一个Circle
:
Circle c3(std::move(c1));
然后你会调用move构造函数。
答案 2 :(得分:0)
新编译器有一个称为 RVO(返回值优化)的特性,并尽量避免无用的副本。 在 g++ 中,如果您在编译命令中放置 -fno-elide-constructors 标志,它将避免它。尽管如此,在您的情况下,即使使用此抑制编译器标志,它也会保留 elide 复制构造函数和移动构造函数。 如果您想查看如何调用 r 值构造函数,请按如下方式使用它并使用上述标志进行编译:
Circle MakeACircle(int r) {
Circle temp(r);
return temp;
}