Consider a class X
with N
member variables, each of some copiable and movable type, and N
corresponding setter functions.
In C++98, the definition of X
would likely look something like this:
class X
{
public:
void set_a(A const& a) { _a = a; }
void set_b(B const& b) { _b = b; }
...
private:
A _a;
B _b;
...
};
Setter functions of class X
above can bind both to lvalue and to rvalue arguments. Depending on the actual argument, this might result in the creation of a temporary and will eventually result in a copy assignment; due to this, non-copiable types are not supported by this design.
With C++11 we have move semantics, perfect forwarding, and universal references (Scott Meyers's terminology), which allow for a more efficient and generalized use of setter functions by rewriting them this way:
class X
{
public:
template<typename T>
void set_a(T&& a) { _a = std::forward<T>(a); }
template<typename T>
void set_b(T&& b) { _b = std::forward<T>(b); }
...
private:
A _a;
B _b;
...
};
Universal references can bind to const
/non-const
, volatile
/non-volatile
, and to any convertible type in general, avoiding the creation of temporaries and passing values straight to operator =
. Non-copiable, movable types are now supported. Possibly undesired bindings can be eliminated either through static_assert
or through std::enable_if
.
So my question is: as a design guideline, should all (let's say, most) setter functions in C++11 be written as function templates accepting universal references?
Apart from the more cumbersome syntax and the impossibility of using Intellisense-like helper tools when writing code in those setter functions, are there any relevant disadvantages with the hypothetical principle "write setter functions as function templates accepting universal references whenever possible"?