Should all/most setter functions in C++11 be written as function templates accepting universal references?

C++C++11Move SemanticsPerfect ForwardingUniversal Reference

C++ Problem Overview


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"?

C++ Solutions


Solution 1 - C++

You know the classes A and B, so you know if they are movable or not and if this design is ultimately necessary. For something like std::string, it's a waste of time changing the existing code unless you know you have a performance problem here. If you're dealing with auto_ptr, then it's time to rip it out and use unique_ptr.

It's usually preferred now to take arguments by value if you don't know anything more specific- such as

void set_a(A a) { _a = std::move(a); }

This permits the use of any of the constructors of A without requiring anything except movability and offers a relatively intuitive interface.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionAndy ProwlView Question on Stackoverflow
Solution 1 - C++PuppyView Answer on Stackoverflow