Saltar a contenido

Last updated:2026-10-31


comments: true keywords: C++23 deducing this,explicit object parameter,recursive lambda,const ref overload description: "C++23 deducing this explained with two verified examples: recursive lambdas and one member function preserving const and value category. Includes compiler requirements and pitfalls." title: C++23 Deducing this: Recursive Lambdas and Member Functions | C++那些事


C++23 deducing this: explicit object parameters

An explicit object parameter lets a member function or lambda name the object it is called on. The common name “deducing this” refers to the version where the type of that object is deduced. It is a C++23 language feature from WG21 P0847R7, not a C++20 lambda feature.

Use it when you need one operation to work across const and value categories, or when a recursive lambda needs to call itself. For an ordinary member function with no such need, a conventional implicit this is simpler.

Recursive lambda without std::function

#include <cassert>

int main() {
    auto factorial = [](this auto const& self, int n) -> int {
        return n <= 1 ? 1 : n * self(n - 1);
    };
    assert(factorial(5) == 120);
}

The first parameter names the lambda object. The call self(n - 1) invokes the same function again; callers still write factorial(5), not factorial(factorial, 5). No type-erased std::function wrapper is needed. This does not prove that every recursive lambda is faster: compare generated code and benchmark your actual workload if performance matters.

One member function for const and value categories

#include <string>
#include <type_traits>
#include <utility>

struct Buffer {
    std::string text;

    template<class Self>
    decltype(auto) get(this Self&& self) {
        return (std::forward<Self>(self).text);
    }
};

static_assert(std::is_same_v<decltype(std::declval<Buffer&>().get()), std::string&>);
static_assert(std::is_same_v<decltype(std::declval<const Buffer&>().get()), const std::string&>);
static_assert(std::is_same_v<decltype(std::declval<Buffer&&>().get()), std::string&&>);

int main() {
    Buffer b{"hello"};
    return b.get() == "hello" ? 0 : 1;
}

Self&& deduces from the caller, and std::forward<Self>(self) keeps its const and lvalue/rvalue category. The parentheses in return (expression); matter: with decltype(auto), they preserve the expression's reference type; without them, this member access can deduce the declared member type and return a copy. Do not retain a reference obtained from a temporary Buffer after that temporary has been destroyed.

Compiler and design boundaries

Compile each example with clang++ -std=c++23 -Wall -Wextra -pedantic example.cpp using Clang 18 or newer. Both examples were compiled and executed with Clang 20.1.0 in Compiler Explorer. The local Apple Clang 16 installation used to build this site does not implement this feature, so a C++23 language flag alone is insufficient there. Check the Clang implementation table or your compiler's feature support before copying the code.

Inside an explicit object member function, use the named parameter (self) rather than an implicit this. Passing the object by value can copy it; choose Self&&, const Self&, or a concrete reference according to ownership and mutation needs. Explicit object member functions cannot be virtual.

Continue with the const family for qualifiers and forwarding, and C++20 lambdas for the pre-C++23 context. Those pages contain shorter examples; this page explains the feature's design and limits.

References