Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Error when using r and l value constructors in a template class

I have a class template like this:

template <typename T>
class MyClass
{
public:
     MyClass(const T & val);  // First
     MyClass(T&& val);        // Second
};

Basically I want MyClass to be constructible from a T whether it's an rvalue or an lvalue. Now when I have something like

const A& foo = ...;
MyClass<const A&> x(foo); 

I get redefinition error for MyClass(const A & val).

I presume this is because T&& is a universal reference and due to reference collapsing rules, the second constructor also gets converted to having the same signature as the first.

Firstly is my understanding of the error scenario correct? secondly how can I get around this problem(I want to able the use the optimizations that move semantics provide when constructing MyClass)?

like image 518
Arun Avatar asked Feb 02 '15 20:02

Arun


Video Answer


1 Answers

I presume this is because T&& is a universal reference ...

Incorrect. T&& is not a universal (forwarding) reference in this case. T&& is exactly an rvalue reference to T. Universal/forwarding references must be deduced.

... and due to reference collapsing rules, the second constructor also gets converted to having the same signature as the first.

This is correct. Our two constructors take:

T const& ==> A const& const& ==> A const&
T&&      ==> A const& &&     ==> A const&

hence the redefinition error.

Depending on what it is you want to do, a simple solution might be to just std::decay T:

template <typename T>
class MyClass
{
    using DecT = typename std::decay<T>::type;

public:
    MyClass(const DecT& );
    MyClass(DecT&& );
};

For your example, that will still create a class with two constructors: one taking const A& and the other taking A&&.

like image 52
Barry Avatar answered Oct 25 '22 04:10

Barry