为什么调用移动分配?
Why move-assignment is called?
我有一些带有复制和移动赋值的类,但是在我的示例中,移动似乎是错误的,并导致未执行的行为。为什么调用移动,我怎样才能避免这种情况? C1 被分配给 C2 并在之后使用,但调用 move 并且 C1 为空。
#include <iostream>
class CSomeClass
{
protected:
size_t m_uiSize = 0u;
public:
CSomeClass() {}
~CSomeClass() {}
size_t size() const { return m_uiSize; }
void resize( size_t p_uiNewSize ) { m_uiSize = p_uiNewSize; }
/* This operator I was expected to be called in all cases. */
CSomeClass& operator=( const CSomeClass& p_rzOther )
{
std::wcout << "Copy explicit" << std::endl;
m_uiSize = p_rzOther.size();
return *this;
}
CSomeClass& operator=( CSomeClass&& p_rzOther )
{
std::wcout << "Move explicit" << std::endl;
m_uiSize = p_rzOther.size();
p_rzOther.resize( 0u );
return *this;
}
#if 1
template<typename M> CSomeClass& operator=( const M& p_rzOther )
{
std::wcout << "Copy UNDEF" << std::endl;
m_uiSize = p_rzOther.size();
return *this;
}
template<typename M> CSomeClass& operator=( M&& p_rzOther )
{
std::wcout << "Move UNDEF" << std::endl;
p_rzOther.resize( 0u );
return *this;
}
#endif
};
int main()
{
CSomeClass C1;
CSomeClass C2;
C1.resize( 1u );
std::wcout << L"C1 size before: " << C2.size() << std::endl;
C2 = C1;
std::wcout << L"C1 size after: " << C2.size() << std::endl;
return 0;
}
这将产生以下输出:
C1 size before: 1
Move UNDEF
C1 size after: 0
我真正的问题有点复杂(有更多的模板和大量的赋值变体(。
如果将#if 1
更改为#if 0
,则会调用正确的复制赋值运算符,但在我的实际代码中,在某些情况下会调用非赋值运算符(相反,也做了一个错误的普通副本(。
我希望你能向我解释这个机制。我错过了什么?
template<typename M> CSomeClass& operator=( M&& p_rzOther )
在这里,M&& p_rzOther
是一个转发参考。您可以将左值和右值传递给它,包括const
和非const
。
在您的情况下,M
被推导出为CSomeClass &
,由于引用折叠,它将赋值运算符转换为:
CSomeClass &operator=(CSomeClass &p_rzOther)
因为在C2 = C1;
中,C1
不是const
,上面的算子比其他两个取const CSomeClass &
赋值算子更匹配。
您可以使用 SFINAE 解决此问题,方法是防止M
被CSomeClass
(可能符合 cv 标准,可能是对一个的引用(:
template <
typename M,
std::enable_if_t<
!std::is_same_v<
CSomeClass,
std::remove_cv_t<std::remove_reference_t<M>>
>,
decltype(nullptr)
> = nullptr
>
CSomeClass &operator=(M &&p_rzOther)
由于这个operator=
可以处理带和不带const
的值类别,因此您不需要另一个。我建议删除
template<typename M> CSomeClass& operator=( const M& p_rzOther )
以防止它与其他运算符冲突。
&&
在模板函数的参数上下文中具有与其他情况下不同的含义。
它被称为转发引用,它将是右值引用或非常量左值引用,具体取决于您传入的内容。
这意味着您的template operator=
是C1 = C2
的最佳匹配,因为两个副本分配都const&
,并且C1
不是const
。
相关文章:
- 自定义先决条件对移动分配运算符有效吗
- C++ - 没有自定义交换功能的移动分配运算符?
- C++中的移动分配出现问题.非法指令: 4.
- 强制复制分配超过移动分配运算符
- 在容器上移动分配:以前包含的对象的状态
- std::元组分配和复制/移动异常保证
- 在之后仍需要使用源对象时调用父移动分配运算符
- vector是否为std::移动的对象连续分配内存
- 为什么调用移动分配?
- 当存在用户定义的移动分配运算符时,已删除模板移动分配运算符
- 移动具有常量成员的类的构造和分配
- C++ - 从移动分配运算符调用复制分配
- 可视化C++将分配移动到未初始化的对象?
- 为什么移动语义与动态 mem 分配中的浅拷贝具有相同的行为
- 我的移动分配操作程序重载的分段错误
- 在预分配的内存中移动数据
- 用于删除复制/移动分配运算符的有效签名
- C++移动分配可防止复制交换习惯用法
- 将C代码和结构初始化和分配移动到C 编译器
- 两个映射之间的分配-移动语义和性能