Пересылка ссылок, квалификаторов 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{}
Обратите внимание, что я использовал {}
так как мне с ними комфортнее и они работают без особого удивления.