为什么要调用移动分配?

问题描述 投票:1回答:2

我有一个带有复制和移动分配的类,但是在我的示例中,移动似乎是错误的,并导致了无法预料的行为。为什么调用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,则会调用正确的副本分配运算符,但是在我的实际代码中,有时会调用非分配运算符(相反,会执行纯拷贝,这是错误的也是如此。

我希望您能向我解释这一机制。我想念什么?

c++ move
2个回答
0
投票

&&在模板函数的参数上下文中与在其他情况下具有不同的含义。

它称为forwarding reference,根据您传入的内容,它将是右值引用或非常量左值引用。

这意味着您的template operator=C1 = C2的最佳匹配,因为两个副本分配都使用const&,而C1不是const


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 )

以防止它与其他运算符冲突。

© www.soinside.com 2019 - 2024. All rights reserved.