Introduction
Since 1989, you could specify modifiers for the *this
object in the function signature, following the argument list.
class C1 {
⋮
int foo (int x);
int foo (int x) const;
Declaring different exact types for this
(C1*
vs const C1*
) is no different than what you do with any other argument with respect to overloading; it just puts the modifiers in a different place. In the example above, you will get the proper version of foo
depending on the attributes of the object you are calling it on:
void bar (const C1& source)
{
C1 dest;
⋮
int resultS = source.foo(); int resultD = dest.foo();
In C++11, there is now another interesting attribute that can be applied to a parameter. Consider the functions:
void demo (C1& param);
void demo (C1&& param);
You can overload based on whether a parameter is an lvalue
or an rvalue
. Again, this selects the version of the overloaded function based on the attributes of the parameter you are calling it on.
C1 get_thing();
C1 me = get_thing();
demo (me); demo (get_thing());
So naturally, this should extend to the implicit this
object, right? Indeed, you can specify the type of the implicit object parameter in this manner (See n4659 §16.3.1 ¶4).
This allows for both overloading and control over calling context. Normally (without any &
or &&
qualifier), a member function doesn’t care about the value category (lvalue
or rvalue
) of the object. But when you specify &
, it then behaves like other parameters and variables of reference type, and refuses to bind non-const rvalues.
What You Can Do With This
Ensure Called on lvalue Only
Sometimes, a member function might be dangerous if called on a temporary instance. In particular, it might return a reference to internal state, or more generally, return a value whose lifetime must be a subset of the object it is obtained from.
class P {
std::string doctorial_thesis;
public:
const std::string& get_text_1() const;
const std::string& get_text_2() & const;
⋮
};
P load_P();
auto report = load_P().get_text_1(); auto report = load_P().get_text_2();
Ensure Called on rvalue
Think about the unique_ptr
template. It must have some awareness of lvalue
vs rvalue
usage in order to allow or disallow certain operations, but this is done with the types of regular parameters. More generally, you can model the intended use case of a resource-returning object, including the implicit this
parameter.
auto handle = get_resource(id).release_raw();
auto res = get_resource(id);
auto handle = res.release_raw(); res.do_something();
Overload for Optimization
You can overload a function to avoid copying when possible, automatically. But, it is still correct in every possible usage.
class P {
std::string doctorial_thesis;
public:
const std::string& get_text() & const
{
return doctorial_thesis; }
const std::string get_text() && const
{ return std::move(doctorial_thesis); }
⋮
};
auto report = load_P().get_text(); report = load_P().get_text();