问题
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:
- Why does the compiler fail to deduce the reference type
SomeType&
in the above situation? - Is there a way to define template function
func
, so that type deduction works with a reference type, without explicit specification of the template parameterT
?
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)
回答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.
回答2:
Use forwarding-reference:
template<typename T>
void func(T && p) //notice &&
{
f(std::forward<T>(p)); //notice std::forward
}
Now please search forwarding-reference (or universal-reference as it is previously referred to) as well as std::forward
on this site to know more about them.
回答3:
Considering the following example:
template<typename T>
void f(T value, T& ref) {// fake swapping
T temp = ref;
ref = value;
value = temp;
}
When compiler tries to deduce the type of T, the reasonable thing to do is firstly removing possible references from "value" and "ref", so that
int i = 1;
int j = 2;
f(i, j); //value of j becomes 1.
//up to here, value of i is still 1
Otherwise, things will get pretty bizarre. If T is otherwise deduced to T&, the type of f(i, j) will be then equivalent with f(int&, int& &). Assuming T& & collapsing to T&, then it becomes f(int&, int&). This is totally against the programmer's intention.
来源:https://stackoverflow.com/questions/37068969/how-do-i-make-template-type-deduction-work-with-references