According to msvc, gcc and clang, the following code is illegal:
template <typename T>
void f(T&& e) {
std::vector<T> v;
// do something with v and e ...
}
int main() {
int i;
f(i);
}
msvc yields
xmemory0(591): error C2528: 'pointer': pointer to reference is illegal
gcc and clang give similar sounding error messages. Note that the universal reference parameter e
is not used. The compiler obviously fails to instantiate the vector v
, complaining about it being used with a reference to int
:
note: see reference to class template instantiation
'std::vector<T,std::allocator<_Ty>>'
being compiled with[ T=int &, _Ty=int & ]
But I can't see where the function template f
is instantiated with a reference to int
.
Can somebody explain the compiler errors that we see here?
A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.
A template argument for a template template parameter is the name of a class template. When the compiler tries to find a template to match the template template argument, it only considers primary class templates. (A primary template is the template that is being specialized.)
For normal code, you would use a class template when you want to create a class that is parameterised by a type, and a function template when you want to create a function that can operate on many different types.
main cannot be a function template; it must be a function.
When f
is called with an lvalue int
, T
will be deduced as int&
, so v
will be a std::vector<int&>
. This is invalid.
One way to get around this is to remove references from T
before using it:
template <typename T>
void f(T&& e) {
using value_type = typename std::remove_reference<T>::type;
//using value_type = std::remove_reference_t<T>; for C++14
std::vector<value_type> v;
// do something with v and e ...
}
However, if you want the function to be as generic as possible, you should use std::decay
instead of std::remove_reference
. This will let f
work with cv-qualified types, arrays and functions.
Scott Meyers explains in this article.
If the expression initializing a universal reference is an lvalue, the universal reference becomes an lvalue reference.
If the expression initializing the universal reference is an rvalue, the universal reference becomes an rvalue reference.
In your case i
is an lvalue and so T
is deduced as int&
.
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