Ir para o conteúdo

💡 Resumo:Lambdas do C++20 (parte 1): sintaxe de templates e restrições de tipos, e como o deducing this do C++23 torna lambdas recursivas numa linha sem std::function.

Última atualização:2026-10-31

Expressões Lambda no C++20 (parte 1)

Templates de lambda no C++20

O C++14 oferece suporte a expressões lambda genéricas, o que permite que uma lambda aceite parâmetros de qualquer tipo, por exemplo:

auto func = [](auto a, auto b) {
    return a + b;
};

seria expandida para:

struct Func {
    template <typename T1, typename T2>
    auto operator()(T1 a, T2 b) const {
        return a + b;
    }
};

Porém, ela não consegue produzir as expansões abaixo:

Primeiro caso: mesmos tipos

template <class T>
auto operator()(T t1, T t2) const;

Segundo caso: apenas std::array

template <class T, std::size_t N>
auto operator()(std::array<T, N> const &) const; 

Terceiro caso: sem dedução

template <class T>
auto operator()() const;

As lambdas do C++14 são muito boas, mas o C++20 nos permite realizar esses casos com facilidade. O C++20 permite que expressões lambda usem parâmetros de tipo template em sua definição; a sintaxe básica é a seguinte:

[]<typename T>(){}

A seguir, alguns exemplos ilustram seus benefícios.

1.Restrição de tipos

Em um exemplo como o abaixo, no C++14 podemos escrever o código a seguir; porém, ao usá-lo, o que é passado não é necessariamente um vector e, assim, o comportamento diverge do esperado, gerando erros.

auto print = [](auto vec) {
  for (auto x : vec) {
    std::cout << x << std::endl;
  }
};

A partir do C++20, podemos restringir o tipo:

auto print2 = []<typename T>(std::vector<T>& vec){
    //使用 std::vector<T> 执行操作
};

Então surge a pergunta, uma questão de entrevista: você sabe como garantir segurança no C++14 para restringir a um vector?

2.Encaminhamento perfeito

No C++20, você pode usar o encaminhamento perfeito com muita facilidade; por exemplo:

auto lambdaFunc = []<typename T>(T&& number) {
  processFunction(std::forward<T>(number));
};

Então surge a pergunta: em comparação com o C++14, qual é a vantagem? Ou seja, o que o C++14 precisaria para implementar o encaminhamento perfeito?

Fim desta seção

Lambdas recursivas com deducing this (C++23)

Uma lambda comum não consegue nomear a si mesma, então recursão significava guardar a lambda num std::function (alocação, indireção) ou envolvê-la num combinador-Y (engenhoso, ilegível). Os parâmetros de objeto explícitos do C++23 — o mesmo recurso deducing this — resolvem isso com elegância: a lambda recebe a si mesma como primeiro parâmetro:

// C++23 explicit object parameter: the lambda receives itself
auto factorial = [](this auto self, int n) -> long long {
    return n <= 1 ? 1 : n * self(n - 1);
};

static_assert(factorial(10) == 3628800);

// before C++23 you needed std::function (heavy) or a Y-combinator (clever)

O parâmetro self é deduzido a cada chamada, então a recursão genérica também funciona: uma lambda pode recursar sobre qualquer tipo, e duas lambdas podem chamar uma à outra passando-se entre si. Como o compilador enxerga através de self, essas chamadas são inlinadas como se você tivesse escrito uma função nomeada.

Suporte: GCC 14+, Clang 18+, MSVC desde o VS 2022 17.2 — compile com -std=c++23.

Perguntas frequentes

Como escrevo uma lambda recursiva em C++?

Desde o C++23, dê à lambda um parâmetro de objeto explícito: [](this auto self, int n) { ... self(n-1) ... }. Antes disso: std::function, um combinador-Y, ou simplesmente uma função nomeada.

A lambda recursiva com deducing this é tão rápida quanto uma função normal?

Na prática, sim: o compilador especializa a chamada através de self e pode inliná-la, ao contrário da chamada indireta de std::function que a recursão pré-C++23 exigia.

Deducing this funciona com lambdas genéricas?

Sim. self é deduzido como auto&& a cada chamada, então a mesma lambda recursa sobre tipos diferentes, e duas lambdas podem até chamar uma à outra passando-se entre si.

Tópicos relacionados

Comentários