I have a template function, func
:
template<typename T>
void func(T p) { f(p); }
And a set of functions f
:
f(SomeType&);
f(int);
...
If I instantiate the template function, func
, using a reference as function argument p
, without explicitly specifying template parameter T
, then the type deduced will not be the reference type of p
, but rather the type p
is a reference to, for example:
SomeType s;
SomeType& ref_to_s = s;
func(ref_to_s); // Type deduction results in: func<SomeType>(ref_to_s)
func<SomeType&>(ref_to_s); // Need to explicitly specify type to work with reference
So, my questions are:
SomeType&
in the above situation?func
, so that type deduction works with a reference type, without explicit specification of the template parameter T
?To be clear, I'd like something working with both (see functions f
above):
func(ref_to_s); // Calls func<SomeType&>(ref_to_s)
func(1); // Calls func<int>(1)
The answer to the first part is that initializing an object from a value and binding a reference to a value are considered equally good (both are "exact matches" in overload resolution), and so the referenciness of a parameter is not deduced. Rather, you must specify it exactly. For a related example consider a hypothetical construction
T x = f();
where T
is (for the duration of this thought experiment) something you're supposed to deduce. What should x
be - an object, or a reference? What would it depend on? The expression on the right has a value, of course, and the type of that value is always an object type. How should you decide? You could query the value category of the expression, but that's too subtle. So instead, you have to say what you want:
T x = f(); // x is an object
T & r = f(); // r is a reference
In C++, this mechanism is actually available if you replace T
with auto
. The same rules apply to template argument deduction for function templates.
Now on to how to solve your problem. The usual idiom is to make the function template always take a reference, and then forward the argument to the inner call:
template <typename T>
void func(T && t)
{
f(std::forward<T>(t));
};
Now func
's parameter is always a reference in any specialization (but thanks to a weird rule called "reference collapsing" it can be either an lvalue or an rvalue reference), and that value is forwarded with the same value category group to f
, and this overload resolution on f
allows lvalue-reference overloads to be selected when the original argument for func
was an lvalue.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With