C++ 在C++;

C++ 在C++;,c++,performance,c++11,c++17,C++,Performance,C++11,C++17,我想写一个模板函数,通过移动或复制来接收参数。 我使用的最有效的方法是: void setA(A a) { m_a = std::move(a); } 在这里,当我们使用的是 A a; setA(a); // <<---- one copy ctor & one move ctor setA(std::move(a)); // <<---- two move ctors 会省很多钱 A a; setA(a);

我想写一个模板函数,通过移动或复制来接收参数。 我使用的最有效的方法是:

void setA(A a)
{
   m_a = std::move(a);
}
在这里,当我们使用的是

A a;
setA(a);            // <<---- one copy ctor & one move ctor
setA(std::move(a)); // <<---- two move ctors
会省很多钱

A a;
setA(a);            // <<---- one copy ctor
setA(std::move(a)); // <<---- one move ctor
有人有什么想法吗?
谢谢

最好是在构造器中就地构造
a
。关于二传手,没有一个是最好的。在大多数情况下,按价值衡量和搬家似乎效果不错,但有时效率较低。如您所示,重载效率最高,但会导致大量代码重复。模板可以在通用引用的帮助下避免代码重复,但随后必须进行自己的类型检查,这会变得很复杂。除非您已经用分析器检测到这是一个瓶颈,否则我建议您坚持按值取然后移动,因为这是最简单的,会导致最小的代码重复,并提供良好的异常安全性。

两个setter版本
setA(a&&a)
setA(const a&a)
可以使用(又称完美转发):

除了更传统的“推送”/“添加”式调用外,标准库还经常提供“安放”式调用。例如,获取构造元素所需的参数,并在向量内部构造一个,而无需复制或移动任何内容。

简短回答:

这是冗长和速度之间的折衷。速度不是一切

以这种方式定义它,有两个函数…将节省很多

A a;
setA(a);            // <<---- one copy ctor
setA(std::move(a)); // <<---- one move ctor
它将保存一个移动分配,这通常不是很多

除非您需要这段特定的代码尽可能快(例如,您正在编写一个自定义容器),否则我更喜欢按值传递,因为它不太冗长

其他可能的办法包括:

  • 使用转发引用,正如其他答案中所建议的那样。它将为您提供与一对重载(
    const T&
    +
    T&&
    )相同的复制/移动量,但它使传递多个参数变得更容易,因为您只需编写一个函数,而不必编写其中的2N个

  • 使setter的行为类似于。这不会给您带来任何性能好处(因为您正在为现有对象赋值,而不是创建一个新对象),因此没有多大意义


    • 经过大量研究,我找到了答案

      我创建了一个高效的包装器类,它允许您保存这两个选项,并允许您在内部函数中决定是否要复制

      #pragma pack(push, 1)
      template<class T>
      class CopyOrMove{
      public:
          CopyOrMove(T&&t):m_move(&t),m_isMove(true){}
          
          CopyOrMove(const T&t):m_reference(&t),m_isMove(false){}
          bool hasInstance()const{ return m_isMove; }
          const T& getConstReference() const {
              return *m_reference;
          } 
          T extract() && {
            if (hasInstance())
                  return std::move(*m_move);
            else
                  return *m_reference;
          }
          
          void fastExtract(T* out) && {
            if (hasInstance())
                  *out = std::move(*m_move);
            else
                  *out = *m_reference;
          }  
      private:
          union
          {
              T* m_move;
              const T* m_reference;
          };
          bool m_isMove;
      };
      #pragma pack(pop)
      
      #pragma包(推送,1)
      模板
      类CopyOrMove{
      公众:
      CopyOrMove(T&&T):m_move(&T),m_isMove(true){}
      CopyOrMove(const T&T):m_reference(&T),m_isMove(false){}
      bool hasInstance()常量{return m_isMove;}
      常量T&getConstReference()常量{
      返回*m_参考;
      } 
      T extract()&&{
      if(hasInstance())
      返回标准::移动(*m_移动);
      其他的
      返回*m_参考;
      }
      无效快速提取(T*out)和{
      if(hasInstance())
      *out=std::move(*m_move);
      其他的
      *out=*m_参考;
      }  
      私人:
      联盟
      {
      T*m_移动;
      常数T*m_参考;
      };
      布尔·穆伊斯莫夫;
      };
      #布拉格语包(流行语)
      
      现在您可以拥有以下功能:

      void setAAndBAndCAndDAndEAndF(CopyOrMove<A> a, CopyOrMove<B> b, CopyOrMove<C> c, CopyOrMove<D> d, CopyOrMove<E> e, CopyOrMove<F> f)
      
      void seta和bandcandeandf(copyrmove a、copyrmove b、copyrmove c、copyrmove d、copyrmove e、copyrmove f)
      

      零代码重复!并且没有多余的复制或移动!

      考虑一下
      void setA(A){std::swap(m_A,A);}
      。最有效的方法是在构造函数中使用完美的转发。@WhozCraig:为什么?用
      m_A
      的先前内容填充
      A
      而不是将其保留为“空”有什么好处,当函数退出时它将被销毁?如果您有一个简单的setter函数,可以将其设置为任何值,那么您可以使用公共数据成员。然后您可以只编写
      obj.a=std::move(a);
      obj.a=a;
      只需一次复制或移动。此外,如果您使setter内联或进行了整个程序优化,并且移动构造函数没有副作用(不应该有副作用),则第二次移动可以优化。“最有效”就是没有一个一开始就需要10个参数的函数。除了参数的副本,在其他地方也会有伤害。只是说。。。
      struct A {
          A(int x) : m_x(x) {}
      
          int m_x;
      };
      
      struct B {
          template<typename T>
          B(T&& a) : m_a(std::forward<T>(a)) {}
      
          A m_a;
      };
      
      int main() {
          B b{ 1 }; // zero copies/moves
      }
      
      #pragma pack(push, 1)
      template<class T>
      class CopyOrMove{
      public:
          CopyOrMove(T&&t):m_move(&t),m_isMove(true){}
          
          CopyOrMove(const T&t):m_reference(&t),m_isMove(false){}
          bool hasInstance()const{ return m_isMove; }
          const T& getConstReference() const {
              return *m_reference;
          } 
          T extract() && {
            if (hasInstance())
                  return std::move(*m_move);
            else
                  return *m_reference;
          }
          
          void fastExtract(T* out) && {
            if (hasInstance())
                  *out = std::move(*m_move);
            else
                  *out = *m_reference;
          }  
      private:
          union
          {
              T* m_move;
              const T* m_reference;
          };
          bool m_isMove;
      };
      #pragma pack(pop)
      
      void setAAndBAndCAndDAndEAndF(CopyOrMove<A> a, CopyOrMove<B> b, CopyOrMove<C> c, CopyOrMove<D> d, CopyOrMove<E> e, CopyOrMove<F> f)