如何为模板的 const ref 成员定义移动赋值运算符 class
How to define move assignment operator for const ref member of template class
我有以下模板 class,其中成员是 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();
}
第一个:
MyClass<int> obj2(std::move(obj)); // will work with move ctor
第二个:
MyClass<int> obj3 = std::move(obj2); // also move ctor called: Why?
两者都在构造对象(分别为obj2
和obj3
)并初始化它们。 =
在此上下文中并不表示赋值。
- Q1
您不能 任何 分配 const &
成员。您可以调用 引用对象的 赋值运算符。
- Q2
这两个都是定义。两者都不是任务。 C++ 有多余的语法。
- Q3
这不是未定义的行为。移出的对象仍然是对象。 "Moving" 一个 int
等同于复制一个 int
,因为没有必要改变来源。当从
移动时,MyClass<std::string>
会打印一个空字符串
请注意,operator=
没有成员初始化程序,因为该对象已经存在。
您似乎正在尝试只移动 std::reference_wrapper
。我认为这不是一个好主意,因为您的 "moves" 实际上只是副本。 C++ 不允许您创建 unique_reference
类型。我能想到的最接近的是 std::unique_ptr<std::reference_wrapper<T>>
,但即便如此,您也无法确保没有其他对基础对象的引用
明确地说,您不能轻松地将包含引用成员的对象浅移动到它拥有的某些内容。
如果它不拥有这些内容,那么您当然可以简单地复制该参考资料;但是如果 move 中的 donor 对象会尝试在销毁时删除引用,那么你就有问题了,我们将进一步讨论。
引用的目标内容本身可能会被移动,然后您的对象移动需要对引用执行移动,创建该引用项的新实例 'live' 和 'killing' 原来的。
另一种选择是使用指针而不是引用。然后您可以轻松地浅移动指针,将施主指针设置为 nullptr。您可以为将存根方法公开给引用的指针创建一个包装器(如果没有太多的话),以保持现有代码的功能。任何直接使用值成员都不会那么容易混淆。
一个非常弱的选择是在你的对象中有一个标志来表示所有权。在移动时标志被清除,并且在销毁时如果标志被清除则引用不会被销毁。弱点是,如果捐赠者在移动后没有立即删除,那么它就会处于不一致状态。 被浅移动的成员可能不再与仍可访问的引用内容兼容。
我有以下模板 class,其中成员是 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();
}
第一个:
MyClass<int> obj2(std::move(obj)); // will work with move ctor
第二个:
MyClass<int> obj3 = std::move(obj2); // also move ctor called: Why?
两者都在构造对象(分别为obj2
和obj3
)并初始化它们。 =
在此上下文中并不表示赋值。
- Q1
您不能 任何 分配 const &
成员。您可以调用 引用对象的 赋值运算符。
- Q2
这两个都是定义。两者都不是任务。 C++ 有多余的语法。
- Q3
这不是未定义的行为。移出的对象仍然是对象。 "Moving" 一个 int
等同于复制一个 int
,因为没有必要改变来源。当从
MyClass<std::string>
会打印一个空字符串
请注意,operator=
没有成员初始化程序,因为该对象已经存在。
您似乎正在尝试只移动 std::reference_wrapper
。我认为这不是一个好主意,因为您的 "moves" 实际上只是副本。 C++ 不允许您创建 unique_reference
类型。我能想到的最接近的是 std::unique_ptr<std::reference_wrapper<T>>
,但即便如此,您也无法确保没有其他对基础对象的引用
明确地说,您不能轻松地将包含引用成员的对象浅移动到它拥有的某些内容。
如果它不拥有这些内容,那么您当然可以简单地复制该参考资料;但是如果 move 中的 donor 对象会尝试在销毁时删除引用,那么你就有问题了,我们将进一步讨论。
引用的目标内容本身可能会被移动,然后您的对象移动需要对引用执行移动,创建该引用项的新实例 'live' 和 'killing' 原来的。
另一种选择是使用指针而不是引用。然后您可以轻松地浅移动指针,将施主指针设置为 nullptr。您可以为将存根方法公开给引用的指针创建一个包装器(如果没有太多的话),以保持现有代码的功能。任何直接使用值成员都不会那么容易混淆。
一个非常弱的选择是在你的对象中有一个标志来表示所有权。在移动时标志被清除,并且在销毁时如果标志被清除则引用不会被销毁。弱点是,如果捐赠者在移动后没有立即删除,那么它就会处于不一致状态。 被浅移动的成员可能不再与仍可访问的引用内容兼容。