8

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

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)),我希望调用第三个构造函数(具有右值引用的复制构造函数),但事实并非如此。显然,由于 c3 已正确实例化,因此调用了一些构造函数,但我不明白为什么编译器不使用显式提供的构造函数。我在这里错过了什么吗?

4

3 回答 3

14

没有调用移动构造函数,因为您的编译器对于您的代码来说太聪明了;)

 Circle c1(2);

这只是用int转换构造函数构造一个对象。

 Circle c2(c1);

这是一个复制操作。c1是一个左值,所以它会引发一个副本。

 Circle c3(Circle(4));

在这里,您的编译器认识到您基本上是在告诉它构造对象两次。所以它省略了一个对象构造函数。在这种情况下,C++ 规范允许这样做。

如果您的编译器无法省略构造,那么它将执行移动。此外,如果您的编译器无法忽略,请将其丢弃。

所以没有任何进展。

于 2012-04-29T17:56:02.697 回答
7

为了获取右值引用,它应该是非常量的,因为构造函数参数的内容将被移动,并且通常这是一个更改操作数状态的操作(尽管不是在您的特定情况下):

Circle(Circle&& c){ }

此外,您在这里看到了复制省略:

Circle c3(Circle(4));

所以移动构造函数不会被调用。这是一个标准的编译器优化,可能会发生也可能不会发生。如果你要构建一个Circle这样的:

Circle c3(std::move(c1));

然后你会调用移动构造函数。

于 2012-04-29T17:28:36.197 回答
0

新的编译器有一个称为 RVO(返回值优化)的特性,并尽量避免无用的副本。在 g++ 中,如果您在编译命令中放置 -fno-elide-constructors 标志,它将避免它。然而,在您的情况下,即使使用此抑制编译器标志,它也会保留省略复制构造函数并移动构造函数。如果您想查看如何调用您的 r-value 构造函数,请按如下方式使用它并使用上面的标志编译它:

Circle MakeACircle(int r) {
      Circle temp(r);
      return temp;
}
于 2021-06-23T11:29:06.153 回答