【问题标题】:Making sure move constructor gets called确保调用移动构造函数
【发布时间】:2016-03-28 00:39:23
【问题描述】:

我有以下简化代码示例:

#include <algorithm>
#include <iostream>

using namespace std;

class ShouldBeMovedWhenSwapped
{
public:
//  ShouldBeMovedWhenSwapped() = default;
//  ShouldBeMovedWhenSwapped(ShouldBeMovedWhenSwapped&&) = default;
//  ShouldBeMovedWhenSwapped(const ShouldBeMovedWhenSwapped&) = default;
//  ShouldBeMovedWhenSwapped& operator=(ShouldBeMovedWhenSwapped&&) = default;

    struct MoveTester
    {
        MoveTester() {}
        MoveTester(const MoveTester&) { cout << "tester copied " << endl; }
        MoveTester(MoveTester&&) { cout << "tester moved " << endl; }
        MoveTester& operator=(MoveTester) { cout << "tester emplaced" << endl; return *this; } // must be declared if move declared
    };

    MoveTester tester;
};

int main()
{
    ShouldBeMovedWhenSwapped a;
    ShouldBeMovedWhenSwapped b;
    std::swap(a,b);
    return 0;
}

我正在使用 MinGW,在运行 'gcc --version' 时我得到 gcc 4.7.2

编辑: 对于第一个问题,请参阅问题中的 cmets。这似乎是 gcc 中的一个错误。

代码的输出取决于哪些构造函数被注释掉了。但我不明白为什么会出现差异。 每个输出背后的原因是什么?

// Everything commented out
tester moved 
tester copied <---- why not moved?
tester emplaced
tester copied <---- why not moved?
tester emplaced

// Nothing commented out
tester moved
tester moved
tester emplaced
tester moved
tester emplaced

// Move constructor commented out
tester copied
tester moved
tester emplaced
tester moved
tester emplaced

对于我的第二个问题(这就是我开始这个测试的原因) - 假设我有一个带有大向量而不是类 MoveTester 的真实案例,我如何确定向量被移动而不是被复制 在这种情况下?

【问题讨论】:

  • 你用的是什么编译器?对我来说everything works as expected
  • 很可能你使用的是旧的 GCC 编译器,我猜是 4.7.x 版本
  • @AntonSavin 谢谢!这是一种快速查看它取决于编译器的方法。我在问题中添加了信息。
  • 至于我的第二个问题?
  • @user1708860:声明移动赋值T&amp; operator=(T&amp;&amp;)会移除移动构造函数T(T&amp;&amp;)的自动生成,因此使用了复制。

标签: c++ c++11 move move-semantics


【解决方案1】:

问题的第一部分是一个过时的编译器,但还有另一个问题:您以次优方式声明 MoveTester::operator= - 它按值获取其参数,因此额外调用了一次复制/移动构造函数。试试这个版本的MoveTester

struct MoveTester
{
    MoveTester() {}
    MoveTester(const MoveTester&) { cout << "tester copied " << endl; }
    MoveTester(MoveTester&&) { cout << "tester moved " << endl; }
    MoveTester& operator=(const MoveTester&) { cout << "tester copy assignment" << endl; return *this; } // must be declared if move declared
    MoveTester& operator=(MoveTester&&) { cout << "tester move assignment" << endl; return *this; } // must be declared if move declared
};

我收到the following output:

tester moved 
tester move assignment
tester move assignment

也许即使使用 GCC 4.7,您也会得到类似的结果。


关于您的第二个问题,std::vector 的移动构造函数按标准保证具有恒定的时间复杂度。问题是编译器是否遵守标准。我相信唯一可以确保的方法是调试或分析您的代码。

【讨论】:

  • 我这样声明是因为“复制和交换成语”stackoverflow.com/questions/3279543/… 但如果我更改运算符,我确实会得到准确的输出
  • 好吧,现在我更困惑了,这似乎是我想要的输出。根据链接的问题,这个版本应该已经产生了一个最佳的代码......
  • @user1708860 这里没有矛盾。您的版本确实会使用 copy&swap 成语,因此在operator= 中您只需交换内容 - 工作量不大。只需更新编译器,您就可以使用原始版本。
  • @user1708860 现在您明白了为什么复制和交换习语在性能方面通常不是最佳的。它的好处是易于实施,并且易于实现强大的异常安全性。
猜你喜欢
  • 2011-05-22
  • 2016-05-23
  • 2012-10-10
  • 2017-11-03
  • 2015-06-27
  • 1970-01-01
  • 2015-02-21
  • 2019-05-27
  • 2012-10-19
相关资源
最近更新 更多