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

Dan*_*hen 5 c++ performance c++11 c++17

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

void setA(A a)
{
   m_a = std::move(a);
}
Run Code Online (Sandbox Code Playgroud)

在这里,当我们使用的是

A a;
setA(a);            // <<---- one copy ctor & one move ctor
setA(std::move(a)); // <<---- two move ctors
Run Code Online (Sandbox Code Playgroud)

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

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
}
Run Code Online (Sandbox Code Playgroud)

会省很多!

A a;
setA(a);            // <<---- one copy ctor
setA(std::move(a)); // <<---- one move ctor
Run Code Online (Sandbox Code Playgroud)

这很棒!对于一个参数...创建具有 10 个参数的函数的最佳方法是什么?!

void setAAndBAndCAndDAndEAndF...()
Run Code Online (Sandbox Code Playgroud)

任何人有任何想法?谢谢!

rus*_*tyx 3

setA(A&& a)可以使用转发参考(也称为完美转发)将两个 setter 版本setA(const A& a)合并为一个版本:

template<typename A>
void setA(A&& a)
{
   m_a = std::forward<A>(a);
}
Run Code Online (Sandbox Code Playgroud)

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

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


话虽如此,请记住 setter 只是常规函数;从技术上讲,当任何 setter 可以被调用时,该对象就已经构造完毕。在 的情况下setA,如果A有一个不平凡的构造函数,那么实例m_a将已经被(默认)构造并且setA实际上必须覆盖它。

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

例如:

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
}
Run Code Online (Sandbox Code Playgroud)

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