如何为模板类的const ref成员定义移动分配运算符

时间:2018-08-14 10:55:19

标签: c++ c++11 language-lawyer move-semantics const-reference

我有以下模板类,其成员为const ref类型。对象的复制已禁用,并且只希望具有移动cntor和移动赋值运算符。

第一季度:如何正确实施const ref type的移动赋值运算符(正确,我做了什么)?

第二季度:为什么这样做

MyClass<int> obj2(std::move(obj));   // will work with move ctor
MyClass<int> obj3 = std::move(obj2); // also move ctor called: Why?

发生了吗?

Q3 :在main()中,可以使用print()调用移动的实例。是UB吗?

我正在使用 Visual Studio 2015(v140)。 这是我的代码:

#include <utility>
#include <iostream>

template<typename Type>
class MyClass
{
    const Type& m_ref;  // const ref type
public:
    explicit MyClass(const Type& arg): m_ref(std::move(arg)){}

    // coping is not allowed
    MyClass(const MyClass&) = delete;
    MyClass& operator=(const MyClass&) = delete;

    // enables move semantics
    MyClass(MyClass &&other) : m_ref(std::move(other.m_ref)) { std::cout << "Move Cotr...\n"; } // works

    // how would I do the move assignment operator, properly: following?
    MyClass& operator=(MyClass &&other)
    {
        // this should have been done in initilizer list(due to const ref member), 
        // but here we cannnot and still it gives no errors, why?

        this->m_ref = std::move(other.m_ref);  
        std::cout << "Move =operator...\n";
        return *this;
    }

    // print the member
    const void print()const noexcept { std::cout << m_ref << std::endl; }
};

//test program
int main() {
    MyClass<int> obj(2);
    MyClass<int> obj2(std::move(obj));   // will work with move ctor
    MyClass<int> obj3 = std::move(obj2); // also move ctor called: Why?

    obj.print();  // why this prints 2? : is it UB?
    obj2.print(); // why this prints 2? : is it UB?
    obj3.print(); // here it makes sence.

    std::cin.get();
}

3 个答案:

答案 0 :(得分:4)

第一个:

MyClass<int> obj2(std::move(obj));   // will work with move ctor

direct initialization

第二个:

MyClass<int> obj3 = std::move(obj2); // also move ctor called: Why?

copy initialization

两者都在构造对象(分别为obj2obj3)并对其进行初始化。 =在此上下文中并不意味着分配。

答案 1 :(得分:4)

  • 第一季度

您不能为const &成员进行任何分配。您可以调用所引用对象的赋值运算符。

  • 第二季度

这两个都是定义。作业也不是。 C ++具有冗余语法。

  • 第三季度

这不是不确定的行为。移出的对象仍然是对象。 “移动” int与复制int相同,因为更改源没有意义。 MyClass<std::string>移出时会显示一个空字符串

需要注意的是,operator=没有成员初始化程序,因为该对象已经存在。

您似乎正在尝试仅移动std::reference_wrapper。我认为这不是一个好主意,因为您的“举动”实际上只是副本。 C ++不允许您创建unique_reference类型。我能想到的最接近的是std::unique_ptr<std::reference_wrapper<T>>,但是即使如此,您也无法确保没有其他对基础对象的引用

答案 2 :(得分:2)

要清楚,您不能轻易地将包含引用成员的对象浅移到其拥有的某些内容。

如果它拥有内容,那么您当然可以简单地复制该引用;但是如果此举中的捐赠者对象试图删除销毁参考文件,则您有问题,我们将进一步讨论。

引用的目标内容本身可能会移动,然后您的对象移动将需要在引用上执行移动,从而创建该引用项的新实例,该实例“处于活动状态”和“杀死状态”是原始的。

另一个选择是使用指针而不是引用。然后,您可以轻松地浅移动指针,将施主指针设置为nullptr。您可以为指针创建一个包装器,以将存根方法公开给引用(如果引用太多),以保持现有代码的功能。任何直接使用值成员的事情都不会那么容易被混淆。

一个非常弱的选择是在您的对象中具有一个表示所有权的标志。在移动时清除标志,而在清除时清除标志不会破坏引用。缺点是,如果在移动后没有立即删除供体,则其状态会不一致。 已经移动的成员可能不再与仍可访问的引用内容兼容。