I want to make a function that takes an optional reference to an object, and creates one for the duration of the function if it is not provided, i.e.
void Foo(Bar& b = Bar()) { /* stuff */ }
This is, of course, invalid code, as a Bar
cannot be implicitly converted to a Bar
reference in this context. The reference can't be const
, as b
is mutated inside the function.
You can get around this by using an rvalue reference, i.e.
void Foo(Bar&& b = Bar()) { /* stuff */ }
Is this a valid use of rvalue references? Callers now have to call std::move
on their Bar
arguments, even though I have no intention of clearing the passed Bar
, as is usually the case when you are passing rvalues.
void Foo(Bar&& b = Bar()) { /* stuff */ }
That's certainly a valid use of r-value references, but it does not in any way reflect the actual semantics, and is thus "broken by design".
What you want to do, is use a forwarder-function supplying the default argument like this:
void Foo(Bar& b) { /* stuff */ }
void Foo() { Bar b{}; Foo(b); }
Or use a static default-argument (Beware that this always reuses the same object):
template<class T> decltype(T{})& default_object() {static T x{}; return x;}
void Foo(Bar& b = default_object<Bar>()) { /* stuff */ }
Or like KerrekSB proposes in a 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 */ }