In a member function of a class template, is T&& an rvalue reference or a forwarding reference?

2.2k views Asked by At

I know that for the following function

template <typename T>
void do_something(T&& arg);

the function parameter is a forwarding reference. But in the following case, is it still a forwarding reference or an rvalue reference?

template <typename T>
class MyClass
{
    void do_something(T&& arg);
};

I think it is still a forwarding reference, but I'm not sure. Furthermore, I'd like to know what can be done to enforce an rvalue reference or a forwarding reference, if the result is not what I intended.

2

There are 2 answers

5
StoryTeller - Unslander Monica On BEST ANSWER

It's an rvalue reference. Forwarding references can only appear in a deduced context. This is just a member function that accepts an rvalue reference to the class template parameter.

You can't force a forwarding reference to be an rvalue reference if you want to maintain template argument deduction for functions. If you don't mind specifying the template argument all over the place, then this will always and only ever give an rvalue reference:

template<typename T> struct identity { using type = T; };

template<typename T> void func(typename identity<T>::type&&);

In retrospect, there actually is a way to maintain deduction but force only rvalue refs to be accepted (besides the self documenting one in Simple's answer). You can provide a deleted lvalue overload:

template<typename T>
void func(T&) = delete;

template<typename T>
void func(T&& s)
{
    // ...
}

The lvalue overload is more specialized when passed an lvalue. And on account of being deleted, will give a somewhat clear error message.

1
Simple On

Furthermore I like to know, what can be done to enforce an rvalue reference

If you always want an rvalue reference in a deduced context (and not a forwarding reference), then you can use this:

template<
    typename T,
    typename = std::enable_if_t<!std::is_lvalue_reference<T>::value>
>
using rval_ref = T&&;

template<typename T>
void foo(rval_ref<T> s)
{
    // ...
}

foo can only be called with an rvalue, and T will not be a reference (i.e. if you call foo with std::string&&, then T will be std::string).