mora
mora

Reputation: 2287

How does it pass lvalue reference to std::forward with CRTP?

I tested std::forward with CRTP(Curiously Reccursive Template Pattern). However I cannot pass lvalue to it. Please read the following code.

#include <algorithm>

struct DataClassA {};

template <typename Derived>
struct ContainerCRTP {
    template <typename Element>
    void add(Element&& tag) { }
};

struct Container : public ContainerCRTP<Container> { };

int main()
{
    DataClassA data_a;
    Container container;

    // Case 1: passing lvalue. compile error.
    container.add<DataClassA>(data_a);

    // Case 2: passing rvalue. It succeeds compiling.
    container.add<DataClassA>(std::move(data_a));                
}

When I pass lvalue in Case 1, gcc compiler gives me a compile error,

template argument deduction/substitution failed:
cannot convert ‘data_a’ (type ‘DataClassA’) to type ‘DataClassA&&’

On the other hand, when I pass rvalue in Case 2, gcc compiler does not give me compiler error.

How can I give lvalue to container.add<DataClassA>(data_a) ?

Upvotes: 1

Views: 133

Answers (1)

M.M
M.M

Reputation: 141648

When you call:

container.add<DataClassA>(data_a);
//            ^^^^^^^^^^^

explicitly providing the template parameter means that the compiler does not deduce the template parameter (i.e. you do not get forwarding reference behaviour).

Instantiating add with DataClassA as template parameter means its signature is:

void add(DataClassA&& tag) 

which is an rvalue reference, not a forwarding reference, therefore an lvalue argument cannot bind to it.

To use perfect forwarding you must let the compiler deduce the template parameter from the argument. There are three possible deductions (T, T&, T&&) based on the value category of the argument and this is how std::forward reproduces the value category of the argument:

container.add(data_a);

Upvotes: 3

Related Questions