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() &&;
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'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.
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.
For class types it is possible to assign to temporary objects which is actually not allowed for built-in types. Further, the assignment operator generated by default even yields an lvalue:
int() = int(); // illegal: "expression is not assignable"
struct B {};
B& b = B() = B(); // compiles OK: yields an lvalue! ... but is wrong! (see below)
For the last statement the result of the assignment operator is actually used to initialize a non-const reference which will become stale immediately after the statement: the reference isn't bound to the temporary object directly (it can't as temporary objects can only be bound to a const or rvalue references) but to the result of the assignment whose life-time isn't extended.
Another problem is that the lvalue returned from the assignment operator doesn't look as if it can be moved although it actually refers to a temporary. If anything is using the result of the assignment to get hold of the value it will be copied rather than moved although it would be entirely viable to move. At this point it is worth noting that the problem is described in terms of the assignment operator because this operator is typically available for value types and returns an lvalue reference. The same problem exists for any function returning a reference to the objects, i.e., *this.
A potential fix is to overload the assignment operator (or other functions returning a reference to the object) to consider the kind of object, e.g.:
class G {
public:
// other members
G& operator=(G) & { /*...*/ return *this; }
G operator=(G) && { /*...*/ return std::move(*this); }
};
The possibility to overload the assignment operators as above has come with C++11 and would prevent the subtle object invalidation noted above and simultaneously allow moving the result of an assignment to a temporary. The implementation of the these two operators is probably identical. Although the implementation is likely to be rather simple (essentially just a swap() of the two objects) it still means extra work raising the question:
Should functions returning a reference to the object (e.g., the assignment operator) observe the rvalueness of the object being assigned to?
An alternatively (mentioned by Simple in a comment) is to not overload the assignment operator but to qualify it explicitly with a & to restrict its use to lvalues:
class GG {
public:
// other members
GG& operator=(GG) & { /*...*/ return *this; }
};
GG g;
g = GG(); // OK
GG() = GG(); // ERROR
IMHO, the original suggestion by Dietmar Kühl (providing overloads for & and && ref-qualifiers) is superior than Simple's one (providing it only for &).
The original idea is:
class G {
public:
// other members
G& operator=(G) & { /*...*/ return *this; }
G operator=(G) && { /*...*/ return std::move(*this); }
};
and Simple has suggested to remove the second overload. Both solutions invalidate this line
G& g = G() = G();
(as wanted) but if the second overload is removed, then these lines also fail to compile:
const G& g1 = G() = G();
G&& g2 = G() = G();
and I see no reason why they shouldn't (there's no lifetime issue as explained in Yakk's post).
I can see only one situation where Simple's suggestion is preferable: when G doesn't have an accessible copy/move constructor. Since most types for which the copy/move assignment operator is accessible also have an accessible copy/move constructor, this situation is quite rare.
Both overloads take the argument by value and there are good reasons for that if G has an accessible copy/move constructor. Suppose for now that G does not have one. In this case the operators should take the argument by const G&.
Unfortunately the second overload (which, as it is, returns by value) should not return a reference (of any type) to *this because the expression to which *this binds to is an rvalue and thus, it's likely to be a temporary whose lifetime is about to expiry. (Recall that forbidding this from happening was one of the OP's motivation.)
In this case, you should remove the second overload (as per Simple's suggestion) otherwise the class doesn't compile (unless the second overload is a template that's never instantiated). Alternatively, we can keep the second overload and define it as deleted. (But why bother since the existence of the overload for & alone is already enough?)
A peripheral point.
What should be the definition of operator = for &&? (We assume again that G has an accessible copy/move constructor.)
As Dietmar Kühl has pointed out and Yakk has explored, the code of the both overloads should be very similar and, in this case, it's better to implement the one for && in terms of the one for &. Since the performance of a move is expected to be no worse than a copy (and since RVO doesn't apply when returning *this) we should return std::move(*this). In summary, a possible one-line definition is:
G operator =(G o) && { return std::move(*this = std::move(o)); }
This is good enough if only G can be assigned to another G or if G has (non-explicit) converting constructors. Otherwise, you should instead consider giving G a (template) forwarding copy/move assignment operator taking an universal reference:
template <typename T>
G operator =(T&& o) && { return std::move(*this = std::forward<T>(o)); }
Although this is not a lot of boiler plate code it's still an annoyance if we have to do that for many classes. To decrease the amount of boiler plate code we can define a macro:
#define ASSIGNMENT_FOR_RVALUE(type) \
template <typename T> \
type operator =(T&& b) && { return std::move(*this = std::forward<T>(b)); }
Then inside G's definition one adds ASSIGNMENT_FOR_RVALUE(G).
(Notice that the relevant type appears only as the return type. In C++14 it can be automatically deduced by the compiler and thus, G and type in the last two code snippets can be replaced by auto. It follows that the macro can become an object-like macro instead of a function-like macro.)
Another way of reducing the amount of boiler plate code is defining a CRTP base class that implements operator = for &&:
template <typename Derived>
struct assignment_for_rvalue {
template <typename T>
Derived operator =(T&& o) && {
return std::move(static_cast<Derived&>(*this) = std::forward<T>(o));
}
};
The boiler plate becomes the inheritance and the using declaration as shown below:
class G : public assignment_for_rvalue<G> {
public:
// other members, possibly including assignment operator overloads for `&`
// but taking arguments of different types and/or value category.
G& operator=(G) & { /*...*/ return *this; }
using assignment_for_rvalue::operator =;
};
Recall that, for some types and contrarily to using ASSIGNMENT_FOR_RVALUE, inheriting from assignment_for_rvalue might have some unwanted consequences on the class layout.
The first problem is that this is not actually ok in C++03:
B& b = B() = B();
in that b is bound to an expired temporary once the line is finished.
The only "safe" way to use this is in a function call:
void foo(B&);
foo( B()=B() );
or something similar, where the line-lifetime of the temporaries is sufficient for the lifetime of what we bind it to.
We can replace the probably inefficient B()=B() syntax with:
template<typename T>
typename std::decay<T>::type& to_lvalue( T&& t ) { return t; }
and now the call looks clearer:
foo( to_lvalue(B()) );
which does it via pure casting. Lifetime is still not extended (I cannot think of a way to manage that), but we don't construct to objects then pointlessly assign one to the other.
So now we sit down and examine these two options:
G operator=(G o) && { return std::move(o); }
G&& operator=(G o) && { *this = std::move(o); return std::move(*this); }
G operator=(G o) && { *this = std::move(o); return std::move(*this); }
which are, as an aside, complete implementations, assuming G& operator=(G o)& exists and is written properly. (Why duplicate code when you don't need to?)
The first and third allows for lifetime extension of the return value, the second uses the lifetime of *this. The second and third modify *this, while the first one does not.
I would claim that the first one is the right answer. Because *this is bound to an rvalue, the caller has stated that it will not be reused, and its state does not matter: changing it is pointless.
The lifetime of first and third means that whomever uses it can extend the lifetime of the returned value, and not be tied to whatever *this's lifetime is.
About the only use the B operator=(B)&& has is that it allows you to treat rvalue and lvalue code relatively uniformly. As a downside, it lets you treat it relatively uniformly in situations where the result may be surprising.
std::forward<T>(t) = std::forward<U>(u);
should probably fail to compile instead of doing something surprising like "not modifying t" when T&& is an rvalue reference. And modifying t when T&& is an rvalue reference is equally wrong.
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.