带有右值引用的C ++构造函数

时间:2012-04-29 17:25:49

标签: c++ constructor c++11 rvalue-reference

考虑这个有三个构造函数的类:

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被正确实例化了但是我 不明白为什么编译器没有使用显式提供的 一。我在这里错过了什么吗?

3 个答案:

答案 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;
}