C++ 具有唯一性的移动语义

C++ 具有唯一性的移动语义,c++,visual-c++,c++11,visual-studio-2012,move-semantics,C++,Visual C++,C++11,Visual Studio 2012,Move Semantics,我使用的是Visual Studio 2012 Update 2,在试图理解std::vector为何尝试使用unique_ptr的复制构造函数时遇到了困难。我已经研究过类似的问题,大多数都与没有显式的move构造函数和/或操作符有关 如果我将成员变量更改为字符串,我可以验证是否调用了move构造函数;但是,尝试使用唯一的_ptr会导致编译错误: 错误C2248:'std::unique\u ptr::unique\u ptr':无法访问在类'std::unique\u ptr'中声明的私有成员

我使用的是Visual Studio 2012 Update 2,在试图理解std::vector为何尝试使用unique_ptr的复制构造函数时遇到了困难。我已经研究过类似的问题,大多数都与没有显式的move构造函数和/或操作符有关

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

错误C2248:'std::unique\u ptr::unique\u ptr':无法访问在类'std::unique\u ptr'中声明的私有成员。

我希望有人能告诉我我错过了什么,谢谢

#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;
}
#包括
#包括
#包括
类MyObject
{
公众:
MyObject():ptr(std::unique_ptr(new int))
{
}
MyObject(MyObject&&other):ptr(std::move(other.ptr))
{
}
MyObject和运算符=(MyObject和其他)
{
ptr=std::move(other.ptr);
归还*这个;
}
私人:
std::唯一的ptr ptr;
};
int main(int argc,char*argv[])
{
std::向量s;
对于(int i=0;i<5;++i)
{
对象o;
s、 推回(o);
}
返回0;
}
函数的
push_back()
按值获取参数。因此,尝试复制构造
push_back()
的参数(如果传递左值),或移动构造它(如果传递右值)

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

要移动对象,您必须写入:

s.push_back(std::move(o));
//          ^^^^^^^^^
在这种情况下,让我惊讶的是,VC11似乎隐式地为
MyObject
生成了一个副本构造函数,而没有将其定义为已删除(从您发布的错误判断)。情况不应该是这样,因为您的类声明了一个移动构造函数。根据C++11标准第12.8/7段,事实上:

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


我必须得出结论,虽然您得到的错误是正确的-因为您没有将右值传递给
push_back()
-VC11在这里不是完全兼容的
o
定义为对象。这意味着它是一个l值。做
s.push_-back(o)
然后调用
push_back()
(它没有其他选择)的l值重载,该重载尝试创建副本

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

for (int i = 0; i < 5; ++i)
{
    MyObject o;
    s.push_back(std::move(o));
}
for(int i=0;i<5;++i)
{
对象o;
s、 推回(标准::移动(o));
}

太棒了!我没有意识到这一点。我仍然有点困惑,为什么只通过将unique_ptr更改为字符串来调用move构造函数。如果VC11正在生成一个隐式复制构造函数,我希望使用它,因为我没有使用std::move。@zYzil:正确的行为是不调用
std::string
(请参阅)的复制构造函数或移动构造函数,因为
MyObject
是不可复制的,您不会从中移动。如果您的代码与我的示例完全相同(包括缺少的
std::move()
),那么VC11有一个bug。我肯定认为VC11中有一个bug。不在GCC 4.8.0上编译;然而,在VC11上,由于调用了Move构造函数,它编译并输出“Move-ctor”。谢谢你的帮助@zYzil:听起来确实像个虫子。祝你的项目好运!VC11尚未实现有关自动创建移动构造函数和禁止自动创建复制构造函数的规则。我认为STL将这些部分称为右值参考3.0,VC11只实现了2.1。如果您想直接在向量中构建对象,您也可以
emplace
它们:
for(int I=0;I<5;++I)s.emplace_back()这也应该适用于VC11。