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

Why copy constructor is called instead of move constructor?

请看下面的示例代码:

   #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 前面,我就得到了正确的结果。我知道在很多情况下采用 const 左值引用比复制构造函数的左值引用更好,但我只想知道发生这种情况的原因。

为什么在这个例子中 Bar & 优于 Bar &&,即使 f() 的 return 值应被视为纯右值?为什么关键字 const 解决了问题? const真的能解决问题吗?跟RVO(Return价值优化)有关系吗?或者,这只是一个编译器错误吗?

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

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

Copy constructor is being called instead of the move constructor

但我还是不明白为什么。

谁能帮帮我?

哇,当我用...编译时...

  1. Visual Studio 在调试中我看到 "Default! Copy!".
  2. Visual Studio 在发布中我看到 "Default!".
  3. 如果您将 Bar(Bar &that) 更改为 Bar(const Bar &that),则 "Default! Move!"
  4. 令人震惊的是,如果您将 Bar(Bar &that) 的顺序与 Bar(Bar &&that) 调换(以便首先定义移动构造函数),那么您实际上会看到 "Default! Move!"

您的问题可能已经得到解答 here

A temporary object cannot bind to a non-const reference. The copy constructor must take a reference to a const object to be able to make copies of temporary objects.

另一件事是临时对象不能修改,因为它们很快就会被销毁。保留对临时对象的引用会导致由于粗心而对不存在的对象进行潜在修改。

这只是 Visual C++ 允许将非常量左值引用绑定到临时对象的常见违规行为。它违反了语言规则,但在被捕获之前花费了太长时间,所以现在有依赖于错误的代码,如果正确修复它就会中断。

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

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