为什么调用复制构造函数而不是移动构造函数

Why copy constructor is called instead of move constructor?

本文关键字:构造函数 移动 复制 为什么 调用      更新时间:2023-10-16

请查看以下示例代码:

   #include <iostream>
    struct Foo {
        Foo() { std::cout << "Default!n"; }
        Foo(const Foo& foo) { std::cout << "Copy!n"; }
        Foo(Foo&& foo) { std::cout << "Move!n"; }
    };
    struct Bar {
        Foo foo;
        Bar() {}
        Bar(Bar &that) : foo(that.foo) {}
        Bar(Bar &&that) : foo(std::move(that.foo)) {}
    };
    Bar f() {
        Bar bar;
        return bar;
    }
    int main() {
        Bar bar(f());
    }

我期望这个代码的输出应该是:

Default!
Move!

但我得到的是:

Default!
Copy!

我看不出为什么要调用复制构造函数而不是移动构造函数。如果我在struct Bar的复制构造函数的声明中将关键字const放在Bar &that之前,我得到了正确的结果。我知道在很多情况下,对于复制构造函数,采用常量左值引用比只采用左值引用更好,但我只想知道发生这种情况的原因。

为什么在这个例子中Bar &优先于Bar &&,即使f()的返回值应该被认为是prvalue?为什么关键字const解决了这个问题?const真的能解决问题吗?这与RVO(返回值优化)有关吗?或者,这只是一个编译器错误吗?

我已经在Visual C++2012年11月的CTP上测试了这个例子。

我在这里发现了一个类似的问题:

正在调用复制构造函数而不是移动构造函数

但我还是不明白为什么。

有人能帮我吗?

这只是Visual C++允许将非常常量左值引用绑定到临时值的常见违规行为。它违反了语言规则,但在被捕获之前已经过了很长时间,所以现在有一些代码依赖于错误,如果修复得当,就会崩溃。

这种错误地允许使用非const复制构造函数的行为,再加上Visual C++版本中不完整的右值引用支持,显然会导致选择错误的重载。

如果你想使用C++11/C++14功能,你最好掌握最新的Visual C++版本。

哇,当我用。。。

  1. Visual Studio在调试中我看到"默认!复制!"
  2. Visual Studio发布版我看到了"默认!"
  3. 如果将Bar(Bar &that)更改为Bar(const Bar &that),则"默认!移动!"
  4. 令人震惊的是,如果您将Bar(Bar &that)的顺序与Bar(Bar &&that)切换(以便首先定义move ctor),那么您实际上会看到"Default!move!"

您的问题可能在这里得到了回答。

临时对象无法绑定到非常量引用。复制构造函数必须引用const对象才能复制临时对象。

另一件事是,临时物体不应被修改,因为它们很快就会被销毁。持有对临时性的引用会导致由于疏忽而对不存在的对象进行潜在的修改。