【问题标题】:Move Semantics with unique_ptr使用 unique_ptr 移动语义
【发布时间】:2013-04-07 07:10:21
【问题描述】:

我正在使用 Visual Studio 2012 Update 2,但无法理解 std::vector 为何尝试使用 unique_ptr 的复制构造函数。我看过类似的问题,大多数都与没有明确的移动构造函数和/或运算符有关。

如果我将成员变量更改为字符串,我可以验证是否调用了移动构造函数;但是,尝试使用 unique_ptr 会导致编译错误:

error C2248: 'std::unique_ptr<_Ty>::unique_ptr' : cannot access private member declared in class 'std::unique_ptr<_Ty>'.

我希望有人能指出我所缺少的东西,谢谢!

#include <vector>
#include <string>
#include <memory>

class MyObject
{
public:
    MyObject() : ptr(std::unique_ptr<int>(new int))
    {
    }

    MyObject(MyObject&& other) : ptr(std::move(other.ptr))
    {
    }

    MyObject& operator=(MyObject&& other)
    {
        ptr = std::move(other.ptr);
        return *this;
    }

private:
    std::unique_ptr<int> ptr;
};

int main(int argc, char* argv[])
{
    std::vector<MyObject> s;
    for (int i = 0; i < 5; ++i)
    {
        MyObject o;
        s.push_back(o);
    }

    return 0;
}

【问题讨论】:

  • 如果你想直接在向量中构造你的对象,你也可以emplace他们:for (int i = 0; i &lt; 5; ++i) s.emplace_back();这也应该适用于VC11。

标签: c++ visual-c++ c++11 visual-studio-2012 move-semantics


【解决方案1】:

push_back() 函数按值获取参数。因此,尝试复制构造 push_back() 的参数(如果您传递的是左值)或移动构造它(如果您传递的是右值)。

在这种情况下,o 是左值 - 因为命名对象是左值 - 右值引用不能绑定到左值。因此,编译器无法调用您的移动构造函数。

为了让你的对象移动,你必须写:

s.push_back(std::move(o));
//          ^^^^^^^^^

在这种情况下让我感到惊讶的是,VC11 似乎为MyObject 隐式生成了一个复制构造函数,而没有将其定义为已删除(从您发布的错误来看)。这不应该是这种情况,因为您的类声明了一个移动构造函数。实际上,根据 C++11 标准的第 12.8/7 段:

如果类定义没有显式声明复制构造函数,则隐式声明。 如果类 定义声明了一个移动构造函数或移动赋值运算符,隐式声明的复制构造函数 被定义为删除;否则定义为默认(8.4)

我必须得出结论,虽然你得到的错误是正确的 - 因为你没有将右值传递给 push_back() - VC11 在这里不完全兼容。

【讨论】:

  • 太棒了!我没有意识到这一点。我仍然有点困惑,为什么仅通过将 unique_ptr 更改为字符串来调用移动构造函数。如果 VC11 正在生成一个隐式复制构造函数,我希望使用它,因为我没有使用 std::move。
  • @zYzil:正确的行为是不调用std::string 的复制构造函数或移动构造函数(参见this live example),因为MyObject 是不可复制的并且您没有移动从中。如果您的代码与我的示例一模一样(包括缺少的std::move()),那么VC11 有一个错误。
  • 我肯定认为VC11有bug。 This example 不能在 GCC 4.8.0 上编译;但是,在 VC11 上,由于调用了移动构造函数,它会编译并输出“Move ctor”。感谢您的所有帮助!
  • @zYzil:听起来确实是个错误。祝你的项目好运!
  • VC11 还没有实现自动创建移动构造函数和禁止自动创建复制构造函数的规则。我认为 STL 将这些部分称为右值引用 3.0,而 VC11 仅实现 2.1。
【解决方案2】:

MyObject o;o 定义为一个对象。这意味着它是一个左值。执行s.push_back(o); 然后调用push_back() 的左值重载(它别无选择),它会尝试创建一个副本。

由于您的类是不可复制的,您必须将对象移动到向量中:

for (int i = 0; i < 5; ++i)
{
    MyObject o;
    s.push_back(std::move(o));
}

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2014-07-13
    • 2020-12-23
    • 1970-01-01
    • 1970-01-01
    • 2014-12-18
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多