【问题标题】:copy constructor not called when returning a std::vector返回 std::vector 时未调用复制构造函数
【发布时间】:2016-08-12 06:42:54
【问题描述】:

我想知道这里发生了什么:

class Test {
public:
    Test() { std::cout << "Constructor" << std::endl; }
    Test(const Test &) { std::cout << "Copy" << std::endl; }
    Test(const Test &&) { std::cout << "Move" << std::endl; }
    ~Test() { std::cout << "Destructor" << std::endl; }
};
std::vector<Test> getTestVektor() {
    std::vector<Test> TestVektor(1);
    return TestVektor;
}
Test getTest() {
    Test TestVariable;
    return TestVariable;
}

int main() {
    {
        std::vector<Test> TestVektor = getTestVektor();
    }
    std::cout << std::endl;
    {
        Test TestVarible = getTest();
    }
    std::cout << std::endl;
    {
        std::vector<Test> TestVektor(1);
        std::vector<Test> TestVektor2 = TestVektor;
    }
    return 0;
}

使用 VisualStudio 2012 编译:

Constructor
Destructor

Constructor
Move
Destructor
Destructor

Constructor
Copy
Destructor
Destructor

可以用复制省略来解释第一种情况。但这与调用移动构造函数的第二种情况相反。

另一种解释是,函数中的 std::vector 释放其内容并将其传递给第二个 std::vector,因此没有调用复制构造函数。但第三种情况表明,情况并非如此。

那么,这里发生了什么?或者这只是一个疯狂的编译器优化?

【问题讨论】:

  • 熟悉“命名返回值优化”吗? (我同意它也应该适用于情况 #2)
  • 复制/移动赋值运算符在哪里?
  • @BenVoigt 是的。显然向量的移动构造函数是重点......
  • @xaxxon 似乎我的编译器隐含地声明了它们......
  • @xaxxon 这是copy initialization

标签: c++ c++11 stdvector


【解决方案1】:

第一种情况(最坏的情况)移动向量(所以只传输内部指针,而不复制/移动Test)。

第三种情况会复制矢量,您必须执行以下操作才能移动它而不是复制:

{
    std::vector<Test> TestVektor(1);
    std::vector<Test> TestVektor2 = std::move(TestVektor);
}

【讨论】:

    【解决方案2】:

    仅供参考clang的输出,带有-O2:

    Constructor
    Destructor
    
    Constructor
    Destructor
    
    Constructor
    Copy
    Destructor
    Destructor
    

    为什么 Visual Studio 会在案例 2 中调用移动对我来说是个谜。您是否禁用了优化?

    【讨论】:

    • 更改为 /O2 并得到相同的 ;-)
    【解决方案3】:

    可以用复制省略来解释第一种情况。

    TestVektor 是从 getTestVektor 返回的临时向量构造的。一个或两个动作都可能被省略。

    但这与调用移动构造函数的第二种情况相反。

    复制/移动省略不是强制性的。它可以用于getTest 的返回和TestVarible 的复制初始化,但它没有用于其中之一。

    我测试的 GCC 和 Clang 版本都省略了。

    另一种解释是,函数中的 std::vector 释放其内容并将其传递给第二个 std::vector

    这正是std::vector 的移动构造函数所做的。

    但第三种情况是复制赋值,而不是移动构造。

    总之,这里发生的事情主要由 std::vector 的移动构造函数解释,但第二种情况也显示了复制/移动省略的可观察(缺乏)副作用。

    【讨论】:

      猜你喜欢
      • 2012-11-03
      • 1970-01-01
      • 1970-01-01
      • 2020-08-06
      • 2018-03-12
      • 2019-08-12
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多