GCC 14 lets a member function deduce its object parameter
Explicit object parameters remove duplicated const and reference-qualified member bodies. GCC 14.1 also implements generated static assertion messages, with separate language-version requirements.
A member function can need the same implementation for mutable and const objects, with further overloads for lvalues and rvalues. The bodies often differ only in which reference type they return. GCC 14.1, released on 7 May, implements explicit object parameters, the C++23 facility commonly called deducing this. It lets the object participate in template deduction while preserving ordinary member-call syntax. That addresses a particular source of repetition in generic library code.
Templates are a substantial part of C++'s value for me. They can express abstractions without requiring a corresponding runtime layer. The difficulty is the source and diagnostic complexity used to obtain that result. An addition that removes repeated overload bodies is useful when its remaining deduction rules can be explained locally.
The object becomes a declared parameter
The GCC 14 C++ changes identify implementation of P0847R7. The paper's motivation includes members such as an optional value accessor with several const and reference-qualified overloads. A free function template can already deduce the corresponding argument properties. The new facility makes that information available through an explicitly declared member object parameter.
The first parameter is prefixed with this. It can name a specific object type or use template deduction. At a call such as object.member(argument), the object supplies that first parameter. The parenthesized argument supplies the next parameter. The public call remains a member invocation even though the implementation can now name its object directly.
Here is an illustrative accessor in a final class. It deliberately keeps the object model small so that the return behavior is the main point.
#include <utility>
class counter final {
int value_ = 0;
public:
template<class Self>
decltype(auto) value(this Self&& self)
{
return (std::forward<Self>(self).value_);
}
};
For a mutable lvalue counter, deduction preserves a mutable lvalue reference to the member. For a const lvalue, it preserves constness. An rvalue invocation can produce an rvalue reference. The parenthesized return expression is intentional with decltype(auto), because the accessor needs the expression's reference category rather than simply the member's declared type.
The function does not extend the lifetime of its object. Retaining a reference obtained from a temporary counter would still require a valid lifetime argument. Deducing the object category makes that category available to the implementation, but does not decide which categories the API should accept. A library can choose a narrower interface when returning references from rvalues is inappropriate.
Forwarding remains a deliberate operation
The named parameter is an expression the function can access. Forwarding uses the deduced type to preserve how the caller supplied the object. This is the same reason a forwarding-reference parameter in another function needs an explicit forwarding operation when passing its argument onward.
Without that operation, a shared implementation can accidentally discard the distinction it was introduced to preserve. The syntactic reduction in overload count does not remove the semantic question of whether a member should be copied, moved or returned by reference. It puts that choice in a body that handles the selected object types.
For a library author, the useful review unit is the return contract for each permitted category. A mutable lvalue may expose mutable access. A const lvalue cannot acquire mutability through deduction. An rvalue may support moving a member, or the API may reject that use. The source needs to choose intentionally instead of treating fewer overloads as sufficient evidence of correctness.
I prefer this to a collection of forwarding wrappers when the operation is truly shared. One body gives a maintainer a single place to inspect the actual algorithm. That advantage is smaller if each object category needs a different policy, in which case separate overloads may still express the design more clearly.
There is no implicit this inside the body
P0847R7 describes explicit-object members without an implicit this pointer available for ordinary member access. The body uses the named object parameter. Such members cannot also be static or virtual, and they do not carry the usual trailing const or reference qualifiers. Those properties are represented through the explicit object parameter instead.
The distinction is useful when reading a declaration. A trailing qualifier on an ordinary member constrains its implicit object. An explicit object declaration writes that constraint as part of the parameter type. The two forms should not be combined as if they independently controlled separate objects.
Deduction can also preserve a derived type. The proposal discusses the consequences for name lookup, including a derived member hiding a base member with the same name. This is why a generic object parameter is more than a shortcut for selecting four qualifiers. Its deduced type can affect which declaration a member access finds.
The example uses a final class to leave that concern outside its contract. In a reusable base class, the implementation needs to decide whether it intends to operate on derived members or the base subobject. Those are different abstractions. An explicit qualification or another constrained design can express the intended access.
Function addresses have a different type
An explicit-object member's address is an ordinary function pointer whose first parameter is the object parameter. P0847R7 contrasts that with the pointer-to-member type of an ordinary member function. Direct source calls can look alike while their indirect-call representations differ.
That matters for adapters and callback interfaces. A template that expects a pointer-to-member may not accept the new function pointer in the same way. A direct invocation through an ordinary function pointer supplies the object as an explicit argument. The source-level similarity of the declarations should not be treated as proof of identical pointer types.
For a public API, changing an existing declaration therefore needs more thought than replacing its body with a template. Consumers may take its address or depend on a particular overload set. The explicit-object facility is useful for new generic interfaces, but adopting it inside an established interface can alter how clients refer to the function.
A lambda can name its own object
P0847R7 also applies the explicit object parameter to lambdas. A recursive lambda can name the closure object through that parameter and use it for the recursive call. This removes the need to arrange a separate named callable merely so that the body can refer to itself. The parameter still follows ordinary deduction rules.
The practical benefit is local definition of a recursive operation with the state it captures. Its recursion depth and termination remain properties of the algorithm. The facility supplies a way to name the callable, without changing those resource requirements. A library deciding whether to use the pattern should therefore consider the call structure independently of how concise the closure declaration becomes. The same distinction applies to moving captured state when a closure is invoked as an rvalue.
Generated assertion messages solve another template problem
The same GCC release implements P2741R3, user-generated static_assert messages, as an initial C++26 facility. This belongs to a different language mode from the C++23 object-parameter addition. The message can be supplied by a suitable constant-expression object with size() and data() operations, instead of only an ordinary string-literal message.
The proposal separates consuming a message from constructing it. It explicitly does not make std::format constexpr. A demonstration that uses generated formatting therefore needs additional library capability before it becomes a valid example for this release. The core language can accept a message representation without supplying every algorithm that might generate that representation.
This distinction is valuable for template authors because it makes diagnostics an interface they can design. A generic assertion can explain the failed condition with more context when a suitable compile-time message producer exists. The project still needs to establish that producer's availability, instead of assuming that acceptance of the new assertion syntax supplies it.
The compiler release contains several compatibility levels
GCC's release notes separately identify C++23 additions and experimental C++26 work. Those categories are useful when defining a project's supported dialect. A library can require explicit object parameters through its C++23 interface while keeping future diagnostic experiments out of headers consumed in that mode.
The object-parameter addition's immediate benefit is specific. It permits one implementation to deduce the caller's object type and reference category. The source still chooses its forwarding behavior and return contract, and indirect users still encounter the resulting function-pointer type. Those details explain both where the feature removes repetition and where adopting it changes an existing interface.