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 asconst &&
? 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?
Suppose we have a type with a
mutable
state. Thenconst&&
will both allow us to mutate that state, and indicate that such mutation is safe.const
is not absolute.Barring mutable state, it is not safe to cast away
const
in aconst&&
method in order to extract state, because extracting state in this way from an actualconst
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.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:
which disables
ref
andcref
for rvalues altogether. We can find these declarations in the draft C++11 standard section20.8
Function objects paragraph 2.Scott Meyers alludes to this use in Universal References in C++11:
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 isconst
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 normalmove
.In many ways the usefulness of the
const&&
may be best evaluated in the context of how useful an object of typeconst T&&
is to begin with. How useful is aconst 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 caseto 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.It is interesting to note that in the proposal C++ library extensions,
optional
, § 5.3, includes overloads, e.g.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 beingconst
or not. Theconst
will need to be dealt with by the contained object being moved or the client code using it. If there is somemutable
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...
= delete
to prohibit the method's use on prvalues and xvalues.&&
method. Advice here is sort from the standard library (and its extensions).Since the method will be performing the same action as the
&&
method, I would advocate that the signature matches the&&
signature.