24 / 163 · C++11 · 8 min
this, Object Qualification, and Chained Interfaces
this is the pointer expression through which an implicit-object member function operates on an object; the type it points to changes with the member function's const qualification. Returning *this can build a chained interface, but it does not extend the object's lifetime. A static member function has no this, and capturing this does not mean owning the object.
In this lesson
A pointer expression, not a hidden field
In an ordinary non-const implicit-object member function, the type of this is X*. In a const member function it is const X*. this is a prvalue expression of pointer type. You cannot assign to this itself, and you must not misstate that property as “the type is always X* const”. Member access such as field can usually be understood as access through the current object.
this is not a pointer field that every object must store. How the implicit object parameter is passed is a matter of calling convention. A static member function has no current object, so it cannot use this. Explicit-object member functions in C++23 also operate on an object through a declared object parameter rather than through implicit this.
const and ref-qualifiers each govern one thing
A const member function restricts modification of ordinary members through that object path. It does not guarantee a globally side-effect-free program, and it does not automatically make an external object pointed to by a member pointer const. The usual way to return the current object is return *this;. When the return type is X&, later operations continue to act on the same object.
The example adds an & ref-qualifier to the mutating operation so that it may be called only on an lvalue. Chained operations then cannot quietly return an lvalue reference from a temporary that is easy to dangle. If rvalues must be supported, design a separate && overload with clear value semantics rather than stuffing every caller into one reference-returning interface.
Lifetime matters more than spelling
Storing this, or capturing this in a lambda, stores an access path; it does not obtain ownership. If a callback runs after the object has been destroyed, accessing members is no longer valid. An asynchronous interface must keep the object alive until the callback finishes, or use a suitable owning object, a weak reference, and an expiration check. Simply adding const does not fix a dangling pointer.
The example verifies that set returns the original object itself, and that a read-only query is invoked through a const reference. The whole chain still depends on the original object remaining alive. Even if a member function body reads no fields, you cannot call an ordinary member function through a null pointer and pretend it is static. Operations that need no object should be declared static.
Pitfalls
- A reference returned from *this does not extend the lifetime of a temporary. Confirm the original object's lifetime before storing a chained result as a long-lived reference.
- const qualifies access rights through the current object. It is not a thread-safety guarantee, and it is not a guarantee of deep immutability.
Run an example
Minimum C++11 · complete program · Download .cpp
#include <cassert>
#include <type_traits>
class Counter {
int value_ = 0;
public:
Counter& set(int value) & {
static_assert(std::is_same<decltype(this), Counter*>::value, "mutable this");
value_ = value;
return *this;
}
int value() const {
static_assert(std::is_same<decltype(this), const Counter*>::value, "const this");
return value_;
}
bool same_object(const Counter& other) const { return this == &other; }
};
int main() {
Counter counter;
Counter& result = counter.set(2).set(5);
const Counter& view = counter;
assert(&result == &counter);
assert(view.value() == 5);
assert(view.same_object(result));
}
Compile locally
g++ -std=c++11 -Wall -Wextra -Wpedantic -pthread objects-this.cpp -o example && ./exampleExpected result
Expected: exit 0, no output; every assert holds.
CHECK YOUR UNDERSTANDING
Close the answer. Explain it.
If you change set's return type to Counter, why can counter.set(2).set(5) no longer work as before?
Show a reference answer
The first call returns a new Counter value, not a reference to the original object. The receiver of the second call is a temporary, which does not satisfy set's & ref-qualifier, so compilation fails. Even if you drop the ref-qualifier, the second call would modify only a copy, and the original counter would still hold two. A chained interface must consider return type, value category, and object identity together.
Check the sources
Drafts and official chapters change. The version mark is only the example’s minimum.