微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

在C ++中创建setter函数的最佳方法

如何解决在C ++中创建setter函数的最佳方法

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

void setA(A a)
{
   m_a = std::move(a);
}

在这里,当我们使用is

A a;
setA(a);            // <<---- one copy ctor & one move ctor
setA(std::move(a)); // <<---- two move ctors

我最近发现以这种方式定义它,并具有两个功能

void setA(A&& a)
{
   m_a = std::move(a);
}
void setA(const A& a)
{
   m_a = a;  // of course we can so "m_a = std::move(a);" too,since it will do nothing
}

会节省很多!

A a;
setA(a);            // <<---- one copy ctor
setA(std::move(a)); // <<---- one move ctor

太好了!一个参数...创建具有10个参数的函数的最佳方法是什么?!

void setAAndBAndCAndDAndEAndF...()

有人有什么想法吗? 谢谢!

解决方法

最好是在构造函数中就地构造a。关于二传手,没有最佳选择。在大多数情况下,按价值行事和搬家似乎行之有效,但有时效率较低。如您所示,重载最大程度地提高了效率,但会导致大量代码重复。模板可以在通用引用的帮助下避免代码重复,但是随后您必须推出自己的类型检查,这会变得很复杂。除非您已将其检测为探查器的瓶颈,否则建议您坚持先按值接后移动,因为它最简单,可导致最少的代码重复并提供良好的异常安全性。

,

可以使用forwarding reference(又称完美转发)将两个设置器版本setA(A&& a)setA(const A& a)合并为一个:

template<typename A>
void setA(A&& a)
{
   m_a = std::forward<A>(a);
}

然后,编译器将根据值类别根据需要合成右值引用或左值引用版本。

这也解决了多值设置器的问题,因为将根据每个参数的值类别来合成正确的设置器。


要说的是,请记住,setter只是常规功能;从技术上讲,该对象在可以调用任何setter时就已经构造好了。在setA的情况下,如果A具有非平凡的构造函数,则实例m_a将已经(默认)构造,setA实际上将必须被覆盖它。

这就是为什么在现代C ++中,重点通常不是在 move-与copy-上,而是在就地构造与移动/复制上。

例如:

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
}

除了更传统的“ push” /“ add”式调用之外,标准库还经常提供“ emplace”式调用。例如,vector::emplace接受构造一个元素所需的参数,并在向量内部构造一个,而不必复制或移动任何东西。

,

简短答案:

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

以这种方式定义它,具有两个功能……将节省很多!

这将保存一个动作分配,通常不会很多。

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

其他可能的方法是:

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

  • 使设置器的行为类似于emplace()。这不会给您带来任何性能上的好处(因为您是分配给一个现有对象而不是创建一个新对象),所以这没有多大意义。

,

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

我制作了一个高效的包装器类,它使您可以同时保留两个选项,并可以在内部函数中确定是否要复制!

#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)

具有零代码重复!而且没有多余的副本或移动!

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。