OMGtechy
OMGtechy

Reputation: 8220

Forwarding references, ref qualifiers and template member functions

Take the following member function:

struct T {
    template <typename X> void f(X&& x) { /* ... */ }
};

In this case, x is a forwarding reference because && is used for the function parameters inside of a template. This is as expected.

Now take this function:

struct T {
    template <typename X> void f(X&& x) && { /* ... */ }
};

I would expect that this would be treated in a similar manner; as a forwarding reference. Therefore, I would expect the following program to compile and run just fine:

#include <iostream>

struct T {
    template <typename X>
    bool operator<(X&& rhs) && {
        std::cout << "&&" << std::endl;
        return true;
    }
};

int main() {
    T t;
    std::cout << (t < T()) << std::endl;
    return 0;
}

But using GCC 4.8.4 and 6.0.1, it does not. Instead, I get the following:

rvalue.cpp: In function ‘int main()’:
rvalue.cpp:13:25: error: passing ‘T’ as ‘this’ argument of ‘bool T::operator<(X&&) && [with X = T]’ discards qualifiers [-fpermissive]
 std::cout << (t < T()) << std::endl;

It would appear that this is not made a forwarding reference. Is this correct or an error? Should this be treated as a forwarding reference? What part of the standard specifies this?

Upvotes: 1

Views: 363

Answers (3)

Barry
Barry

Reputation: 303057

The two &&s` here are treated differently:

struct T {
    template <typename X> void f(X&& x) && { /* ... */ }
};

You are correct in that x is a forwarding reference. But the && on the right is a qualification on the object instance. In this case, f() can only be invoked if the object instance is an rvalue (perhaps adding to the confusion is that the first && takes x as a forwarding reference but the second && takes the implicit object parameter as an rvalue reference). That is:

T().f(4); // ok

T t;
t.f(4); // error

This works the same way as const qualification does:

struct X { void f(); };
const X cx;
cx.f(); // error

Upvotes: 3

Sarfaraz Nawaz
Sarfaraz Nawaz

Reputation: 361472

The member operator<() && can be called on rvalue only, so the following:

t < T()

is not going to work, because t is not rvalue — it is lvalue.

The following should work:

std::move(t) < T()

and this too:

T{} < T{}

Note that I've used {} as I'm more comfortable with them and they work without much surprise.

Upvotes: 0

NathanOliver
NathanOliver

Reputation: 180630

The reference qualifier at the end of the function states that operator< should only be called when the LHS is a temporary. Remember a operator can be called like any other member function so what you have is

t.operator<(T())

and t is not a temporary.

If we change your example to

std::cout << (T() < t) << std::endl;

Then it works just fine as the object you are calling the operator< on is a temporary object.

Upvotes: 2

Related Questions