Dear Readers!

Today I finally found the definite guideline (and as such a solution) to a problem, that has been bugging me since I was introduced to universal references (Scott Meyers nomenclature) or also known as forwarding references (“a lot of others”’s nomenclature?).

A sidenote:
I’m not sure which name is the “official” right now, as there was a formal document by Sutter, Stroustrup and Dos Reis back in 2014 to call these “forwarding references”, but as of the writing of the post (4 years later), google search gives about 10x more hits for “universal references” than for the other. I like “forwarding reference” more (less confusing in my opinion), but since “universal reference” is used more often, I’ll just stick with the latter.

So, what was exactly my problem with universal references? It is simple: where am I ought to use them? To me, it is quite tempting to give a bit more “optimization” to the code, especially if there is a (from first glance) simple feature for this. With this mindset one can achieve quite the contrary though. Let’s get back to the GumballMachine I discussed in my last post:

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
// in foo.h:

class GumballMachine {
public:
    // constructors...

    static auto create(std::string llocation, unsigned int numberGumballs) -> std::unique_ptr<GumballMachine>;
    // other machinery...

private:
    template <typename S>
    GumballMachine(S&& llocation, unsigned int numberGumballs)
        : location(std::forward<S>(llocation)), ballCount(numberGumballs) {}

    std::string location;
    unsigned int ballCount = 0;
    // other states...
};

// in foo.cpp::

auto GumballMachine::create(std::string llocation, unsigned int numberGumballs) -> std::unique_ptr<GumballMachine>
{
    std::unique_ptr<GumballMachine> instance(new GumballMachine(std::move(llocation), numberGumballs));

    // other initializations in instance...
}

So basically we have a static factory with pass-by-value parameters, which calls a private constructor with a universal reference. The basic idea here is, that we make a copy of the “outside” string, which in turn can safely be moved (passed on as rvalue) into the class constructor, which will forward it to the member string move constructor. One allocation (let’s omit STO this time) and one move: doesn’t seem that bad. What is the problem then? Before we think about it, let’s see a summary of the possible options we can choose from (I took the examples from Mr. Sutter’s CppCon 2014 slides, which can be found here):

C++

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
/*
 * Examples from Mr. Sutter's slides:
 */

class employee {
    std::string name_;
    
public:
    void set_name(/* ?? */);
};

// option #1:
void set_name(const std::string& name) {name_ = name;}

// option #2:
void set_name(const std::string& name) {name_ = name;}
void set_name(std::string&& name) noexcept {name_ = std::move(name);}

// option #3:
void set_name(std::string name) noexcept {name_ = std::move(name);}

// option #4:
template <class String, class = std::enable_if_t<!std::is_same<std::decay_t<String>,std::string>::value>>
void set_name(String&& name) noexcept(std::is_nothrow_assignable<std::string&, String>::value)
    {name_ = std::forward<String>(name);}

Option #4 for is not a joke, it is a template fully constrained for strings. The noexcept keyword in option #3 is dangerous! For details (and benchmarks!) of the above options please consult the linked slides. Now that we have several choices, we can ask which one of them is the recommended way? Mr. Sutter provided a nice table to answer this, which he encourages to stick onto the wall of your cubicle (pic taken from slides linked above):

Function parameters guideMr. Sutter’s guide for function parameter usage

By looking back at the GumballMachine code, we can already see some problems. For one, the pass-by-value approach for the string in the static method should be replaced by option #1 at least. This will remove the inevitable need for allocation in case of passed in long strings. Second, the private constructor should also use pass-by-reference for the string (the template in turn must also go), as perfect forwarding is nowhere to be seen on the table. Or should it be replaced? There is an addendum to the above rules, which you can only see in its entirety if you watch the conference video as well (which is highly recommended). I took a printscreen of it for convenience:

Function parameters guide extraMr. Sutter’s extended guide for function parameter usage

It would seem, that in special cases (e.g. in library classes Mr. Sutter explains it), perfect forwarding can also be used. I think my case with the private constructor falls under that category, so it can remain. But what about all that extra code regarding constraints we see in option #4? In the case of the GumballMachine class I think it is safe to omit them, as the constructor is private and only the static factory calls it, a factory that provides a std::string no matter what. This way the factory provides all the constraints we need, and in turn everything is well.

Was the template part intentional? I’d like to say yes, but reality is, it was accidental. It is only later I found out that this case was a special case. So what did I learn from all this?

Use the defaults, they usually work just fine.

Thanks for reading.