C++11 makes it possible to overload member functions based on reference qualifiers:
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
I understand how this works, but what is a use case for it?
I see that N2819 proposed limiting most assignment operators in the standard library to lvalue targets (i.e., adding "&
" reference qualifiers to assignment operators), but this was rejected. So that was a potential use case where the committee decided not to go with it. So, again, what is a reasonable use case?
Overloaded functions enable you to supply different semantics for a function, depending on the types and number of its arguments. For example, consider a print function that takes a std::string argument. This function might perform very different tasks than a function that takes an argument of type double .
Operators which can be overloaded either as members or as non-members. This is the bulk of the operators: The pre- and post-increment/-decrement operator++() , operator--() , operator++(int) , operator--(int) The [unary] dereference operator*()
When binary operators are overloaded through a member function they take one explicit argument. If overloaded through a friend function they instead take two explicit arguments. We do not use friend functions to overload some specific operators. However, member functions can be used to overload them.
Function overloading is a feature that allows us to have same function more than once in a program. Overloaded functions have same name but their signature must be different.
In a class that provides reference-getters, ref-qualifier overloading can activate move semantics when extracting from an rvalue. E.g.:
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();
This does seem like a lot of effort to invest only to have the shorter syntax
auto hhc = factory().get();
have the same effect as
auto hhc = std::move(factory().get());
EDIT: I found the original proposal paper, it provides three motivating examples:
operator =
to lvalues (TemplateRex's answer)operator &
to lvalues. I suppose this is sensible to ensure that the "pointee" is more likely to be alive when the "pointer" is eventually dereferenced:struct S { T operator &() &; }; int main() { S foo; auto p1 = &foo; // Ok auto p2 = &S(); // Error }
Can't say I've ever personally used an operator&
overload.
One use case is to prohibit assignment to temporaries
// 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; }
whereas not using the reference qualifier would leave you the choice between two bads
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
The first choice allows move semantics, but acts differently on user-defined types than on builtins (doesn't do as the ints do). The second choice would stop the assigment but eliminate move semantics (possible performance hit for e.g. matrix multiplication).
The links by @dyp in the comments also provide an extended discussion on using the other (&&
) overload, which can be useful if you want to assign to (either lvalue or rvalue) references.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With