Пересылка ссылок, квалификаторов ref и функций-членов шаблона

Возьмите следующую функцию-член:

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

В этом случае, x является ссылкой для пересылки, потому что && используется для параметров функции внутри шаблона. Это как и ожидалось.

Теперь возьмите эту функцию:

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

Я ожидаю, что this будет рассматриваться аналогичным образом; в качестве экспедиционной ссылки. Поэтому я ожидал бы, что следующая программа скомпилируется и будет работать нормально:

#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;
}

Но, используя GCC 4.8.4 и 6.0.1, это не так. Вместо этого я получаю следующее:

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;

Казалось бы, что this не делается ссылка на пересылку. Это правильно или ошибка? Должен this трактовать как ссылку для пересылки? Какая часть стандарта определяет это?

3 ответа

Решение

Два &&s` здесь трактуются по-разному:

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

Вы правы в этом x является ссылкой для пересылки. Но && справа - квалификация экземпляра объекта. В этом случае, f() может быть вызван, только если экземпляр объекта является rvalue (возможно, добавление к путанице заключается в том, что первый && принимает x в качестве ссылки для пересылки, но второй && принимает неявный параметр объекта как ссылку на значение). То есть:

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

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

Это работает так же, как const квалификация делает:

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

Спецификатор ссылки в конце функции утверждает, что operator< следует вызывать только тогда, когда LHS является временным. Помните, что оператор может быть вызван как любая другая функция-член, так что у вас есть

t.operator<(T())

а также t не временный.

Если мы изменим ваш пример на

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

Тогда он работает как объект, который вы называете operator< это временный объект.

Член operator<() && может вызываться только по rvalue, поэтому следующее:

t < T()

не собирается работать, потому что t это не значение - это значение.

Следующее должно работать:

std::move(t) < T()

и это тоже

T{} < T{}

Обратите внимание, что я использовал {} так как мне с ними комфортнее и они работают без особого удивления.

Другие вопросы по тегам