Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using a class template in a function template with universal reference parameter

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?

like image 558
Angle.Bracket Avatar asked Feb 15 '16 10:02

Angle.Bracket


People also ask

Can a template parameter be a function?

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.

Can a template be a template parameter?

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.)

What is the difference between class template and function template?

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.

Can we use template in main function?

main cannot be a function template; it must be a function.


2 Answers

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.

like image 117
TartanLlama Avatar answered Nov 16 '22 02:11

TartanLlama


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&.

like image 21
Mohamad Elghawi Avatar answered Nov 16 '22 02:11

Mohamad Elghawi