I've been digging around ref-qualifiers a bit, following on a previous question.
Given the code sample below;
#include <iostream>
#include <string>
#include <utility>
struct A {
std::string abc = "abc";
std::string& get() & {
std::cout << "get() &" << std::endl;
return abc;
}
std::string get() && {
std::cout << "get() &&" << std::endl;
return std::move(abc);
}
std::string const& get() const & {
std::cout << "get() const &" << std::endl;
return abc;
}
std::string get() const && {
std::cout << "get() const &&" << std::endl;
return abc;
}
};
int main()
{
A a1;
a1.get();
const A a2{};
a2.get();
A().get();
const A a3{};
std::move(a3).get();
}
And the output is as you would expect:
get() &
get() const &
get() &&
get() const &&
This compiles and runs with clang and gcc 4.9.1 (not 4.9.0 though). Live sample here.
In general code (the sample is there to see how the code compiles and runs).
What would the purpose of the const && ref-qualifier on a method be?
The method is unable to modify the contents on the object (it is const), an attempt to return std::move(abc); from the const && method doesn't actually move the std::string at all. Presumably you would want to be able modify the object, since it's an r-value and won't be around for long. If the const && qualified method were to be removed, the code std::move(a3).method() would bind to the const & qualified method, which would make sense.
What, if any, would the implied semantic difference be between a method qualified as const & and one qualified as const &&? I.e. how would the implementation vary or why would you want both?
Would the std::string truely be able to be "moved" out of the temporary object?
What would a "canonical" signature look like for std::string get() const && in this case?
On the usefulness of const&&... (in general)
The usefulness of the const&& qualifier on the member method is minimal at best. The object cannot be modified in the same manner as a && method would allow it to be modified; it is const after all (as noted, mutable does change this). So we will not be able to rip out its guts, since the temporary is expiring anyway, as we would in something akin to a normal move.
In many ways the usefulness of the const&& may be best evaluated in the context of how useful an object of type const T&& is to begin with. How useful is a const T&& function argument? As pointed out in another answer (to this question) here, they are very useful in declaring functions deleted, e.g. in this case
template <class T> void ref (const T&&) = delete;
to explicitly disallow objects of prvalue and xvalue value category types from being used with the functions, and const T&& does bind to all prvalue and xvalue objects.
What is the usefulness of const&& method qualifier?
It is interesting to note that in the proposal C++ library extensions, optional, § 5.3, includes overloads, e.g.
constexpr T value() const &&;
that are qualified as const&& and are specified to perform the same action as the && alternative.
The reason I can infer for this case; is that this is for completeness and correctness. If the value() method is called on an rvalue, then it performs the same action independent of it being const or not. The const will need to be dealt with by the contained object being moved or the client code using it. If there is some mutable state with the object being contained, then that state can legitimately be changed.
There may well still be some merit in this; in no particular order...
To declare it = delete to prohibit the method's use on prvalues and xvalues.
If the type has mutable state and the qualifier makes sense (possibly in addition to the other qualifiers) in the target environment, consider it.
If you are implementing a generic container type, then for completeness and correctness, consider adding it and performing the same action as the && method. Advice here is sort from the standard library (and its extensions).
What would a "canonical" signature look like for a const&& qualified method?
Since the method will be performing the same action as the && method, I would advocate that the signature matches the && signature.
We can find a similar exploration of this issue in the article What are const rvalue references good for? and the one use that stood out is this example form the standard library:
template <class T> void ref (const T&&) = delete;
template <class T> void cref (const T&&) = delete;
which disables ref and cref for rvalues altogether. We can find these declarations in the draft C++11 standard section 20.8 Function objects paragraph 2.
Scott Meyers alludes to this use in Universal References in C++11:
Even the simple addition of a const qualifier is enough to disable the
interpretation of “&&” as a universal reference:
Suppose we have a type with a mutable state. Then const&& will both allow us to mutate that state, and indicate that such mutation is safe.
struct bar;
struct foo {
mutable std::vector<char> state;
operator bar() const&;
operator bar() const&&;
};
const is not absolute.
Barring mutable state, it is not safe to cast away const in a const&& method in order to extract state, because extracting state in this way from an actual const object is undefined behavior.
I see two main uses for ref-qualifying a method. One is like you show in your get() && method, where you use it to select a potentially more efficient implementation that is only available when you know the object will no longer be used. But the other is a safety hint to prevent calling certain methods on temporary objects.
You can use notation like get() const && = delete in such cases, although realistically I would save this approach for modifying methods, especially those that are potentially costly. It doesn't make much sense to mutate and then discard an object without retrieving something, and doubly so if it's expensive to perform the mutation. This construct gives the compiler a way to flag and prevent such usage.
Related
I've been digging around ref-qualifiers a bit, following on a previous question.
Given the code sample below;
#include <iostream>
#include <string>
#include <utility>
struct A {
std::string abc = "abc";
std::string& get() & {
std::cout << "get() &" << std::endl;
return abc;
}
std::string get() && {
std::cout << "get() &&" << std::endl;
return std::move(abc);
}
std::string const& get() const & {
std::cout << "get() const &" << std::endl;
return abc;
}
std::string get() const && {
std::cout << "get() const &&" << std::endl;
return abc;
}
};
int main()
{
A a1;
a1.get();
const A a2{};
a2.get();
A().get();
const A a3{};
std::move(a3).get();
}
And the output is as you would expect:
get() &
get() const &
get() &&
get() const &&
This compiles and runs with clang and gcc 4.9.1 (not 4.9.0 though). Live sample here.
In general code (the sample is there to see how the code compiles and runs).
What would the purpose of the const && ref-qualifier on a method be?
The method is unable to modify the contents on the object (it is const), an attempt to return std::move(abc); from the const && method doesn't actually move the std::string at all. Presumably you would want to be able modify the object, since it's an r-value and won't be around for long. If the const && qualified method were to be removed, the code std::move(a3).method() would bind to the const & qualified method, which would make sense.
What, if any, would the implied semantic difference be between a method qualified as const & and one qualified as const &&? I.e. how would the implementation vary or why would you want both?
Would the std::string truely be able to be "moved" out of the temporary object?
What would a "canonical" signature look like for std::string get() const && in this case?
On the usefulness of const&&... (in general)
The usefulness of the const&& qualifier on the member method is minimal at best. The object cannot be modified in the same manner as a && method would allow it to be modified; it is const after all (as noted, mutable does change this). So we will not be able to rip out its guts, since the temporary is expiring anyway, as we would in something akin to a normal move.
In many ways the usefulness of the const&& may be best evaluated in the context of how useful an object of type const T&& is to begin with. How useful is a const T&& function argument? As pointed out in another answer (to this question) here, they are very useful in declaring functions deleted, e.g. in this case
template <class T> void ref (const T&&) = delete;
to explicitly disallow objects of prvalue and xvalue value category types from being used with the functions, and const T&& does bind to all prvalue and xvalue objects.
What is the usefulness of const&& method qualifier?
It is interesting to note that in the proposal C++ library extensions, optional, § 5.3, includes overloads, e.g.
constexpr T value() const &&;
that are qualified as const&& and are specified to perform the same action as the && alternative.
The reason I can infer for this case; is that this is for completeness and correctness. If the value() method is called on an rvalue, then it performs the same action independent of it being const or not. The const will need to be dealt with by the contained object being moved or the client code using it. If there is some mutable state with the object being contained, then that state can legitimately be changed.
There may well still be some merit in this; in no particular order...
To declare it = delete to prohibit the method's use on prvalues and xvalues.
If the type has mutable state and the qualifier makes sense (possibly in addition to the other qualifiers) in the target environment, consider it.
If you are implementing a generic container type, then for completeness and correctness, consider adding it and performing the same action as the && method. Advice here is sort from the standard library (and its extensions).
What would a "canonical" signature look like for a const&& qualified method?
Since the method will be performing the same action as the && method, I would advocate that the signature matches the && signature.
We can find a similar exploration of this issue in the article What are const rvalue references good for? and the one use that stood out is this example form the standard library:
template <class T> void ref (const T&&) = delete;
template <class T> void cref (const T&&) = delete;
which disables ref and cref for rvalues altogether. We can find these declarations in the draft C++11 standard section 20.8 Function objects paragraph 2.
Scott Meyers alludes to this use in Universal References in C++11:
Even the simple addition of a const qualifier is enough to disable the
interpretation of “&&” as a universal reference:
Suppose we have a type with a mutable state. Then const&& will both allow us to mutate that state, and indicate that such mutation is safe.
struct bar;
struct foo {
mutable std::vector<char> state;
operator bar() const&;
operator bar() const&&;
};
const is not absolute.
Barring mutable state, it is not safe to cast away const in a const&& method in order to extract state, because extracting state in this way from an actual const object is undefined behavior.
I see two main uses for ref-qualifying a method. One is like you show in your get() && method, where you use it to select a potentially more efficient implementation that is only available when you know the object will no longer be used. But the other is a safety hint to prevent calling certain methods on temporary objects.
You can use notation like get() const && = delete in such cases, although realistically I would save this approach for modifying methods, especially those that are potentially costly. It doesn't make much sense to mutate and then discard an object without retrieving something, and doubly so if it's expensive to perform the mutation. This construct gives the compiler a way to flag and prevent such usage.
I don't know which C++ standard presented this feature, but I cannot think of any use cases of this.
A member functions with && modifier will only be considered by overload resolution if the object is rvalue
struct Foo
{
auto func() && {...}
};
auto a = Foo{};
a.func(); // Does not compile, 'a' is not an rvalue
std::move(a).func(); // Compiles
Foo{}.func(); // Compiles
Can someone please explain the use case for this?
Why we ever want some routine to be performed only for rvalues?
Ref-qualification was added in c++11. In general, propagation of the value-category is incredibly useful for generic programming!
Semantically, ref-qualifications on functions help to convey the intent; acting on lvalue references or rvalues -- which is analogous to const or volatile qualifications of functions. These also parallel the behavior of struct members, which propagate the qualifiers and categories.
A great example of this in practice is std::optional, which provides the std::optional::value() function, which propagates the value-category to the reference on extraction:
auto x = std::move(opt).value(); // retrieves a T&&
This is analogous to member-access with structs, where the value-category is propagated on access:
struct Data {
std::string value;
};
auto data = Data{};
auto string = std::move(data).value; // expression yields a std::string&&
In terms of generic composition, this massively simplifies cases where the input may be an lvalue or an rvalue. For example, consider the case of using forwarding references:
// Gets the internal value from 'optional'
template <typename Optional>
auto call(Optional&& opt) {
// will be lvalue or rvalue depending on what 'opt' resolves as
return std::forward<Optional>(opt).value();
}
Without ref-qualification, the only way to accomplish the above code would be to create two static branches -- either with if constexpr or tag-dispatch, or some other means. Something like:
template <typename Optional>
auto call(Optional&& opt) {
if constexpr (std::is_lvalue_reference_v<Optional>) {
return opt.value();
} else {
return std::move(opt.value());
}
}
On a technical level, rvalue-qualifications on functions provides the opportunity to optimize code with move-constructions and avoid copies in a semantically clear way.
Much like when you see a std::move(x) on a value, you are to expect that x is expiring; it's not unreasonable to expect that std::move(x).get_something() will cause x to do the same.
If you combine && overloads with const & overloads, then you can represent both immutable copying, and mutating movements in an API. Take, for example, the humble "Builder" pattern. Often, Builder pattern objects hold onto pieces of data that will be fed into the object on construction. This necessitates copies, whether shallow or deep, during construction. For large objects, this can be quite costly:
class Builder {
private:
// Will be copied during construction
expensive_data m_expensive_state;
...
public:
auto add_expensive_data(...) -> Builder&;
auto add_other_data(...) -> Builder&;
...
auto build() && -> ExpensiveObject {
// Move the expensive-state, which is cheaper.
return ExpensiveObject{std::move(m_expensive_state), ...}
}
auto build() const & -> ExpensiveObject
// Copies the expensive-state, whcih is costly
return ExpensiveObject{m_expensive_state, ...}
}
...
};
Without rvalue-qualifications, you are forced to make a choice on the implementation:
Do destructive actions like moves in a non-const function, and just document the safety (and hope the API isn't called wrong), or
Just copy everything, to be safe
With rvalue-qualifications, it becomes an optional feature of the caller, and it is clear from the authored code what the intent is -- without requiring documentation:
// Uses the data from 'builder'. May be costly and involves copies
auto inefficient = builder.build();
// Consumes the data from 'builder', but makes 'efficient's construction
// more efficient.
auto efficient = std::move(builder).build();
As an added benefit, static-analysis can often detect use-after-move cases, and so an accidental use of builder after the std::move can be better caught than simple documentation could.
What are the use cases of class member functions marked &&?
A use case are "getter" style functions. They traditionally return references to sub objects or owned objects whose lifetime is tied to the super object.
That is however sometimes problematic, since calling the getter on an rvalue will result in a reference to an object that will be destroyed at the end of the expression. That is unsafe:
struct demo {
int& get_mem() { return mem; }
private:
int mem;
};
demo get_demo();
int& ref = get_demo().get_mem();
std::cout << ref; // bug
An lvalue ref qualifier prevents such bugs:
int& get_mem() & { return mem; }
int& ref = get_demo().get_mem(); // safely ill-formed
But it also prevents working previously correct code such as:
std::cout << get_demo().get_mem(); // used to be OK; now ill-formed
A solution is to provide two overloads, each with different qualifier:
int& get_mem() & { return mem; }
int get_mem() && { return mem; }
It is commonly used with types that wrap other types, with a full set of overloads that simulate all constess and value categories:
const int& get_mem() const& ;
int& get_mem() & ;
const int&& get_mem() const&&;
int&& get_mem() &&;
I've already asked on code review and software engineering but the topic didn't fit the site, so I'm asking here hoping this is not opinion-based. I am an "old school" C++ developer (I've stopped at C++ 2003) but now I've read a few books on modern C++ 11/17 and I'm rewriting some libraries of mine.
The first thing I've made is adding move constructor/assignment operator where needed ( = classes that already had destructor + copy constructor and copy assignment). Basically I'm using the rule of five.
Most of my functions are declared like
func(const std::string& s);
Which is the common way to pass a reference avoiding a copy. By the way there is also the new move semantic and there's somethig that I wasn't able to find in my books/online. This code:
void fun(std::string& x) {
x.append(" world");
std::cout << x;
}
int main()
{
std::string s{"Hello "};
fun(s);
}
Can also be written as:
void fun(std::string&& x) {
x.append(" world");
std::cout << x;
}
int main()
{
std::string s{"Hello "};
fun(std::move(s));
//or fun("Hello ");
// or fun(std::string {"Hello" });
}
My question is: when should I declare functions that accept a paramenter that is a rvalue reference?
I understand the usage of && semantic on constructors and assignment operators but not really on functions. In the example above (first function) I have a std::string& x which cannot be called as fun("Hello "); of course because I should delcare the type as const std::string& x. But now the const doesnt allow me to change the string!
Yes, I could use a const cast but I rarely do casts (and if it's the case, they're dynamic casts). The power of the && is that I avoid copies, I don't have to do something like
std::string x = "...";
fun(x); //void fun(std::string& x) {}
and I can assing temporary values that will be moved. Should I declare functions with rvalue references when possible?
I have a library that I'm rewriting with modern C++ 17 and I have functions like:
//only const-ref
Type1 func(const type2& x);
Type3 function(const type4& x);
I am asking if it's worth rewriting all of them as
//const-ref AND rvalue reference
Type1 func(const type2& x);
Type3 function(const type4& x);
Type1 func(type2&& x);
Type3 function(type4&& x);
I don't want to create too many overloads that may be useless but if an user of my library wanted to use the move operation I should create the && param types. Of course I am not doing this for primitive types (int, double, char...) but for containers or classes. What do you suggest?
I am not sure if the latter scenario (with both versions) would be useful or not.
Let me comment on four scenarios in your question and examples.
std::string_view with pass-by-value is supposed to replace const std::string& parameters and whenever you can guarantee the necessary preconditions for a safe usage of std::string_view (lifetime, pointee doesn't change), it's a good candidate to start modernizing your function signatures.
const T& vs. T&& (where T is not subject to template type deduction) with known usage scenarios. The void fun function that appends to a given, modifiable string, will only makes sense as void fun(std::string&&) if calling code doesn't need the result after the call. In this case, the rvalue-reference signature documents this expectation nicely and is the way to go. But these cases are rather rare in my experience.
const T& vs. T&& (again, no type deduction) with unknown usage scenarios. A good reference here is std::vector::push_back, which is overloaded for both rvalue and lvalue references. The push_back operation is assumed to be cheap compared to move-construction a T, that's why the overload makes sense. When a function is assumed to be more expensive than such a move-construction, passing the argument by value is a simplification that can make sense (see also Item 41 in EMC++).
const T& vs. T&& when type deduction takes place. Here, use universal references together with std::forward whenever possible and the parameters can't be const qualified. If they aren't modified in the function body, go with const T&.
You want to use rvalue references only if:
You might retain a copy and you need the extra performance (measure!)
Example for this would be writing a library type (e.g. std::vector) where performance matters to its users.
You want only temporaries to be passed to your function
Example for this is the move assignment operator: After the assignment, the original objects state will not exist anymore.
Forwarding references (T&& with T deduced) fall under the first option.
Rvalue reference (not to be confused with a forwarding reference!) in function arguments is used when there is a need to move ownership from one object to another.
It is true that it is often done in context of move constructors/assignment operators, but this is not the only case. For example, a function accepting an ownership of std::unique_prt could accept it's argument by an rvalue reference.
Background
The following code block appears in Scott Meyers' famous book "Effective C++" Item 3:
class TextBlock {
public:
...
const char& operator[](std::size_t position) const
{
... // do bounds checking
... // log access data
... // verify data integrity
return text[position];
}
char& operator[](std::size_t position)
{
... // do bounds checking
... // log access data
... // verify data integrity
return text[position];
}
...
private:
std::string text;
};
The author states that in the above implementation, the contents of the const and the non-const overloads are essentially the same. In order to avoid code duplication, it could be simplified like this:
class TextBlock {
public:
...
const char& operator[](std::size_t position) const // the same as before
{
...
...
...
return text[position];
}
char& operator[](std::size_t position) // now just calls const op[]
{
return // cast away const on
const_cast<char&>( // op[]'s return type;
static_cast<const TextBlock&>(*this) // add const to *this's type;
[position] // call const version of op[]
);
}
...
private:
std::string text;
};
Questions
My questions are:
When shall we need an overload for const T& and another for T&&? (Here, T may be a template parameter or a class type, so T&& may or may not mean a universal reference) I can see that in the standard library, many classes provide both overloads. Examples are constructors of std::pair and std::tuple, there are tons of overloads. (Okay, I know that among the functions, one of them is the copy constructor and one of them is the move constructor.)
Is there a similar trick to share the implementations for the const T& and T&& overloads? I mean, if the const T&& overload returns an object that is copy-constructed, and the T&& overload returns something that is move-constructed, after sharing the implementation, this property must still hold. (Just like the above trick: const returns const and non-const returns non-const, both before and after implementation sharing)
Thanks!
Clarifications
The two overloads I'm referring to should look like:
Gadget f(Widget const& w);
Gadget f(Widget&& w);
It is nothing related to returning by rvalue references, that is:
Widget&& g(/* ... */);
(By the way, that question was addressed in my previous post)
In f() above, if Gadget is both copy-constructible and move-constructible, there's no way (except from reading the implementation) to tell whether the return value is copy-constructed or move-constructed. It is nothing to deal with Return Value Optimization (RVO) / Named Return Value Optimization (NRVO). (See my previous post)
References
Effective C++
std::pair::pair
std::tuple::tuple
When is it a good time to return by rvalue references?
• When shall we need an overload for const T& and another for T&&?
Basically, when moving gives you a performance gain, there should be also a move constructor. The same holds for functions in which you'd otherwise needed an expensive copy.
In your example, where you return a reference to a char, it is however not advisable to also set up a function which returns an rvalue reference. Rather, return by value and rely on the compiler's ability to apply RVO (see e.g. here)
•Is there a similar trick to share the implementations for the const T& and T&& overloads?
I often found it useful to set up a constructor or function using a universal reference (I'm lazy), i.e. something like
struct MyClass
{
template<typename T /*, here possibly use SFINAE to allow only for certain types */>
MyClass(T&& _t) : t(std::forward<T>(_t)) {}
private:
SomeType t;
};
EDIT: Regarding your update: if you have an expensive copy of Widget in your function f, it is adviceable also to provide an overload taking a Widget&&.
Gadget f(Widget const& w)
{
Widget temp = w; //expensive copy
}
Gadget f(Widget&& w)
{
Widget temp = std::move(w); //move
}
You could combine both function using a function template like this
template<typename WidgetType
// possibly drop that SFINAE stuff
// (as it is already checked in the first assignment)
, typename std::enable_if<std::is_convertible<std::remove_reference_t<WidgetType>, Widget>::value> >
Gadget(WidgetType&& w)
{
Widget temp = std::forward<WidgetType>(w);
//or
std::remove_reference_t<WidgetType> temp2 = std::forward<WidgetType>(w);
}
... I didn't say it's nicer ;-).
EDIT 2: See also this thread, which addresses your question much more thouroughly.
Now that GCC 4.8.1 and Clang 2.9 and higher support them, reference qualifiers (also known as "rvalue references for *this") have become more widely available. They allow classes to behave even more like built-in types by, e.g., disallowing assignment to rvalues (which can otherwise cause an unwanted cast of an rvalue to an lvalue):
class A
{
// ...
public:
A& operator=(A const& o) &
{
// ...
return *this;
}
};
In general, it is sensible to call a const member function of an rvalue, so an lvalue reference qualifier would be out of place (unless the rvalue qualifier can be used for an optimization such as moving a member out of a class instead of returning a copy).
On the flip side, mutating operators such as the pre decrement/increment operators should be lvalue-qualified, as they usually return an lvalue-reference to the object. Hence also the question: Are there any reasons to ever allow mutating/non-const methods (including operators) to be called on rvalue references aside from conceptually const methods which are only not marked const because const-correctness (including proper application of mutable when using an internal cache, which may include ensuring certain thread-saftey guarantees now) was neglected in the code base?
To clarify, I am not suggesting to forbid mutating methods on rvalues on the language level (at the very least this could break legacy code) but I believe that defaulting (as an idiom / coding style) to only allowing lvalues for mutating methods will generally lead to cleaner, safer APIs. However I am interested in examples where not doing so leads to cleaner, less astonishing APIs.
A mutator that operates on an R-value can be useful if the R-value is used to accomplish some task, but in the interim it maintains some state. For example:
struct StringFormatter {
StringFormatter &addString(string const &) &;
StringFormatter &&addString(string const &) &&;
StringFormatter &addNumber(int) &;
StringFormatter &&addNumber(int) &&;
string finish() &;
string finish() &&;
};
int main() {
string message = StringFormatter()
.addString("The answer is: ")
.addNumber(42)
.finish();
cout << message << endl;
}
By allowing either an L-value or an R-value, one can construct an object, pass it through some mutators, and use the result of the expression to accomplish some task without having to store it in an L-value, even if the mutators are member functions.
Also note that not all mutating operators return a reference to the self. User-defined mutators can implement any signature they need or want. A mutator may consume the state of the object to return something more useful, and by acting on an R-value, the fact that the object is consumed isn't a problem since the state would have otherwise been discarded. In fact, a member function that consumes the state of the object to produce something else useful will have to be marked as such, making it easier to see when l-values are consumed. For example:
MagicBuilder mbuilder("foo", "bar");
// Shouldn't compile (because it silently consumes mbuilder's state):
// MagicThing thing = mbuilder.construct();
// Good (the consumption of mbuilder is explicit):
MagicThing thing = move(mbuilder).construct();
I think it comes about in cases where the only way to retrieve some value is by mutating another value. For instance, iterators don't provide a "+1" or a "next" method. So suppose I'm constructing a wrapper for stl list iterators (perhaps to create an iterator for my own list-backed data-structure):
class my_iter{
private:
std::list::iterator<T> iter;
void assign_to_next(std::list::iterator<T>&& rhs) {
iter = std::move(++rhs);
}
};
Here, the assign_to_next method takes an iterator and assigns this one to have the next position after that one. It's not too hard to imagine situations where this might be useful, but more importantly there is nothing surprising about this implementation. True, we could also say iter = std::move(rhs); ++iter; or ++(iter = std::move(rhs));, but I don't see any arguments for why those would be any cleaner or faster. I think this implementation is the most natural to me.
FWIW HIC++ agrees with you as far as assignment operators:
http://www.codingstandard.com/rule/12-5-7-declare-assignment-operators-with-the-ref-qualifier/
Should a non-const method ever apply to rvalues?
This question puzzles me. A more sensible question to me would be:
Should a const method ever apply exclusively to rvalues?
To which I believe the answer is no. I can't imagine a situation in which you would want to overload on const rvalue *this, just as I can't imagine a situation in which you would want to overload on const rvalue arguments.
You overload on rvalues because it's possible to handle them more efficiently when you know that you can steal their guts, but you can't steal the guts of a const object.
There are four possible ways to overload on *this:
struct foo {
void bar() &;
void bar() &&;
void bar() const &;
void bar() const &&;
};
The constness of the latter two overloads means that neither one can mutate *this, so there can be no difference between what the const & overload is allowed to do to *this and what the const && overload is allowed to do to *this. In the absence of the const && overload, the const & will bind to both lvalues and rvalues anyway.
Given that overloading on const && is useless and only really provided for completeness (prove me wrong!) we are left with only one remaining use case for ref-qualifiers: overloading on non-const rvalue *this. One can define a function body for a && overload, or one can = delete it (this happens implicitly if only a & overload is provided). I can imagine plenty of cases in which defining a && function body might be useful.
A proxy object which implements pointer semantics by overloading operator-> and unary operator*, such as boost::detail::operator_arrow_dispatch, might find it useful to use ref-qualifiers on its operator*:
template <typename T>
struct proxy {
proxy(T obj) : m_obj(std::move(obj)) {}
T const* operator->() const { return &m_obj; }
T operator*() const& { return m_obj; }
T operator*() && { return std::move(m_obj); }
private:
T m_obj;
};
If *this is a rvalue then operator* can return by move instead of by copy.
I can imagine functions that move from the actual object to a parameter.