C ++ 11 позволяет перегружать функции-члены на основе ссылочных квалификаторов:
class Foo {
public:
void f() &; // for when *this is an lvalue
void f() &&; // for when *this is an rvalue
};
Foo obj;
obj.f(); // calls lvalue overload
std::move(obj).f(); // calls rvalue overload
Я понимаю, как это работает, но какой вариант использования для этого?
я вижу это N2819 предложено ограничить большинство операторов присваивания в стандартной библиотеке целевыми значениями l (т.е. добавив «&
«ссылки на классификаторы для операторов присваивания), но это было отклонено. Так что это был потенциальный случай использования, когда комитет решил не соглашаться с этим. Итак, опять же, что является разумным вариантом использования?
В классе, который предоставляет методы получения ссылок, перегрузка ref-квалификатора может активировать семантику перемещения при извлечении из значения r. Например.:
class some_class {
huge_heavy_class hhc;
public:
huge_heavy_class& get() & {
return hhc;
}
huge_heavy_class const& get() const& {
return hhc;
}
huge_heavy_class&& get() && {
return std::move(hhc);
}
};
some_class factory();
auto hhc = factory().get();
Это похоже на большие усилия, чтобы инвестировать только для того, чтобы иметь более короткий синтаксис
auto hhc = factory().get();
имеют тот же эффект, что и
auto hhc = std::move(factory().get());
РЕДАКТИРОВАТЬ: я нашел оригинал предложения, это дает три мотивирующих примера:
operator =
в lvalues (ответ TemplateRex)operator &
ценностям Я полагаю, что это разумно, чтобы гарантировать, что «pointee», скорее всего, будет жив, когда «указатель» в конечном итоге разыменовывается:struct S {
T operator &() &;
};
int main() {
S foo;
auto p1 = &foo; // Ok
auto p2 = &S(); // Error
}
Не могу сказать, что я когда-либо лично использовал operator&
перегрузки.
Одним из вариантов использования является запретить назначение временным
// can only be used with lvalues
T& operator*=(T const& other) & { /* ... */ return *this; }
// not possible to do (a * b) = c;
T operator*(T const& lhs, T const& rhs) { return lhs *= rhs; }
в то время как не использование ссылочного квалификатора оставит вам выбор между двумя плохими
T operator*(T const& lhs, T const& rhs); // can be used on rvalues
const T operator*(T const& lhs, T const& rhs); // inhibits move semantics
Первый вариант позволяет перемещать семантику, но действует по-другому на определенные пользователем типы, чем на встроенные (не так, как int). Второй выбор остановит назначение, но исключит семантику перемещения (возможное снижение производительности, например, при умножении матриц).
Ссылки @dyp в комментариях также обеспечивают расширенное обсуждение использования других (&&
) перегрузка, которая может быть полезна, если вы хотите назначить на (или lvalue или rvalue) ссылки.
Если для f () требуется Foo temp, которая является его копией и изменена, вы можете изменить temp this
вместо этого, пока вы не можете иначе
С одной стороны, вы можете использовать их для предотвращения семантически бессмысленных функций для вызова временных функций, таких как operator=
или функции, которые изменяют внутреннее состояние и возвращают void
, добавляя &
в качестве эталонного классификатора.
С другой стороны, вы можете использовать его для оптимизации, например, для перемещения члена из объекта в качестве возвращаемого значения, когда у вас есть ссылка на rvalue, например, функция getName
может вернуть либо std::string const&
или же std::string&&
в зависимости от ссылочного классификатора.
Другим вариантом использования могут быть операторы и функции, которые возвращают ссылку на исходный объект, например Foo& operator+=(Foo&)
который может быть специализированным, чтобы вместо этого возвращать ссылку на rvalue, делая результат подвижным, что снова будет оптимизацией.
TL; DR: используйте это, чтобы предотвратить неправильное использование функции или для оптимизации.