繁体   English   中英

为什么叫移动赋值?

[英]Why move-assignment is called?

我有一些带有复制和移动分配的课程,但在我的示例中移动似乎是错误的并导致意外行为。 为什么要调用 move,我怎样才能避免这种情况? 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 operator=C1 = C2的最佳匹配,因为两个复制赋值都采用const& ,而C1不是const

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 )

以免与其他运营商发生冲突。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM