c++ - 为什么调用 move 分配?

标签 c++ move

我有一些带有复制和 move 分配的类(class),但 move 在我的示例中似乎是错误的,并导致意外的行为。为什么调用 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<typename M> CSomeClass& operator=( M&& p_rzOther )

这里,M&& p_rzOtherforwarding reference 。您可以向其传递左值和右值,包括 const 和非 const

在您的情况下,M 被推导为 CSomeClass &,这是由于 reference collapsing将赋值运算符变成:

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 )

防止与其他运算符发生冲突。

关于c++ - 为什么调用 move 分配?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/59109960/

相关文章:

c++ - 缺少着色器 C 兼容性是否重要?

c++ - 遍历区域并在 OpenCV 中获取平均像素值?

c++ - 为什么在这种情况下需要指针?

c++ - 如何 move 其中包含 Promise 的结构?

java - java中如何将特定文件 move 到新文件夹

c++ - 在 C++ 中插入和删除整数中的逗号

python - 使用 pybind11 在多线程 C++ 程序中嵌入 Python 解释器

c++ - 更改保持原始指针指向其字段的对象的地址

css 更改大小会通过 move 其余的 div 来影响

vim:如何既不使用 ctrl+w+ 也不更改默认键设置来 move 窗口