【问题标题】:How to define move assignment operator for const ref member of template class如何为模板类的 const ref 成员定义移动赋值运算符
【发布时间】:2019-01-21 05:08:16
【问题描述】:

我有以下模板类,其中成员为const ref 类型。对象的复制被禁用,只需要移动 cntor 和移动赋值运算符。

Q1:如何正确实现const ref type的移动赋值运算符(对吗,我做的)?

Q2:为什么会这样

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();
}

【问题讨论】:

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


    【解决方案1】:

    第一个:

    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)并初始化它们。 = 在这种情况下并不意味着赋值。

    【讨论】:

    • 这很有帮助
    【解决方案2】:
    • 第一季度

    您不能任何分配const &amp; 成员。您可以调用被引用对象的赋值运算符。

    • 第二季度

    这两个都是定义。也不是任务。 C++ 有多余的语法。

    • 第三季度

    这不是未定义的行为。移出的对象仍然是对象。 “移动”int 与复制int 相同,因为更改源没有意义。 MyClass&lt;std::string&gt; 在移出时会打印一个空字符串

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

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

    【讨论】:

      【解决方案3】:

      需要明确的是,您不能轻易地将包含引用成员的对象浅移动到它拥有的某些内容。

      如果它拥有该内容,那么您当然可以简单地复制该引用;但是如果移动中的施主对象会尝试在销毁时删除引用,那么您就有问题了,我们将进一步讨论。

      可能引用的目标内容本身可以移动,然后您的对象移动需要在引用上执行移动,创建该引用项目的新实例,该实例是“活动的”和“正在杀死的” '原来的。

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

      一个非常弱的选项是在你的对象中有一个表示所有权的标志。在移动时,标志被清除,而在销毁时,如果标志被清除,引用不会被销毁。弱点是,如果捐助者在移动后没有立即被删除,那么它处于不一致的状态。 被浅移动的成员可能不再与仍可访问的引用内容兼容。

      【讨论】:

        猜你喜欢
        • 2021-08-30
        • 1970-01-01
        • 2017-08-03
        • 2019-06-30
        • 2011-11-14
        • 2015-01-27
        • 2012-02-11
        • 2011-10-20
        相关资源
        最近更新 更多