Using rvalue references for default arguments

I want to make a function that takes an optional reference to an object and creates it throughout the function if it is not provided, i.e.

void Foo(Bar& b = Bar()) { /* stuff */ } 

This, of course, is invalid code, because Bar cannot be implicitly converted to a Bar reference in this context. The reference cannot be const , since b mutated inside the function.

You can get around this using the rvalue link, i.e.

 void Foo(Bar&& b = Bar()) { /* stuff */ } 

Is this a valid use of rvalue references? Now callers have to call std::move in their Bar arguments, although I am not going to clear the passed Bar , as is usually the case with rvalues.

+5
source share
2 answers
 void Foo(Bar&& b = Bar()) { /* stuff */ } 

This, of course, is the proper use of r-value references, but it does not in any way reflect the actual semantics and, thus, is "disrupted by design."

What you want to do is use a forwarder function that supplies the default argument as follows:

 void Foo(Bar& b) { /* stuff */ } void Foo() { Bar b{}; Foo(b); } 

Or use the default static argument (fear that this always repeats the same object):

 template<class T> decltype(T{})& default_object() {static T x{}; return x;} void Foo(Bar& b = default_object<Bar>()) { /* stuff */ } 

Or as suggested by KerrekSB in the comment (I added constexpr ), use this dangerous template function:

 template<class T> constexpr T& no_move(T&& t) { return t; } void Foo(Bar& b = no_move(Bar{})) { /* stuff */ } 
+9
source

So you have a function that takes an in-out parameter, which it is going to change in order to pass information to the caller.

But you want the parameter to be optional.

So your solution is to force the parameter to appear as the in parameter, requiring the callers to move the arguments (which would usually mean that they lost any state that they had or might be in an unspecified state). This is a bad, bad design. You will confuse callers with a weird API created for the convenience of internal implementation of the function. You should design an API for users, not developers.

You can either do what the Deduplicator offers and divide it into two functions: one that provides a dummy object that will be provided as an in-out parameter and then discarded:

 void Foo(Bar& b) { /* stuff */ } void Foo() { Bar dummy{}; Foo(dummy); } 

or since what you think is necessary is a link that may be null, stop using the link and use the correct language function to pass anything by reference, which may be null:

 void Foo(Bar* b) { /* stuff, updating b if not null */ } 
+7
source

Source: https://habr.com/ru/post/1200061/


All Articles