Ir para o conteúdo

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

Dedução de argumentos de template de classe CTAD no C++17

Introdução

A metaprogramação de templates em C++ sempre foi uma funcionalidade bem conhecida dos desenvolvedores C++: tanto iniciantes quanto desenvolvedores avançados podem escrever código genérico com parâmetros de tipo por meio dela. Com a dedução de argumentos de template de classe (CTAD) introduzida no C++17, o trabalho com templates de classe tornou-se mais intuitivo e flexível.

Todo o código desta seção também será atualizado na comunidade.

O C++17, ao introduzir o CTAD, traz os seguintes benefícios:

Por exemplo: um código tão complexo quanto este

std::vector<FooBar<int, const char*>> obj{a, b, c}; 

precisa apenas de:

 std::vector obj{a, b, c};

Ou ainda, por exemplo:

std::tuple<int>

pode ser simplificado para:

std::tuple t1{1};

Portanto, vamos conhecer o CTAD hoje.

CTAD é a abreviação de Class template argument deduction, dedução de argumentos de template de classe; basta você fornecer ao compilador um deduction guide (guia de dedução) e já poderemos usar esse recurso.

Se não fornecermos, como no exemplo abaixo:

#include <iostream>
template <typename T, typename U>
struct MyPair {
  T first{};
  U second{};
};

int main() {
  MyPair<int, int> p1{1, 2};
  MyPair p2{1, 2};
  std::cout << p1.first << ", " << p1.second << std::endl;
  std::cout << p2.first << ", " << p2.second << std::endl;
  return 0;
}

Para p2, obteríamos o erro:

no viable constructor or deduction guide for deduction of template arguments of 'MyPair'

E como corrigir isso?

Basta adicionar um deduction guide, escrevendo da seguinte forma:

template <typename T, typename U>
MyPair(T, U) -> MyPair<T, U>;

A dedução de argumentos de template de classe (CTAD) simplifica a instanciação de templates de classe ao permitir que o compilador deduza os argumentos de template a partir dos argumentos do construtor. Antes da introdução do CTAD, os desenvolvedores tinham de especificar explicitamente os argumentos de template na instanciação. Com o CTAD, essa especificação explícita deixa de ser necessária, tornando o código mais legível e fácil de manter.

template <typename T>
class Add{
    private:
        T first;
        T second;
    public:
        Add() = default;
        Add(T first, T second): first_{first}, second_{second} {}
        T result() const { return first + second; }
};

int main(){
    Add one(1,2);               // T被推导为int
    Add two{1.245, 3.1415};     // T被推导为double
    Add three = {0.24f, 0.34f}; // T被推导为float
}

No exemplo acima, o CTAD permite que o tipo T seja deduzido com base nos argumentos do construtor, eliminando a necessidade de especificação explícita de tipo.

Inicialização de membros não estáticos

Porém, há um cenário em que o CTAD não pode ser usado: a inicialização de membros não estáticos. Por exemplo: na classe Test, para usar o MyPair de fora, é preciso escrever o tipo completo.

template <typename T = int, typename U = int>
struct MyPair {
  T first{};
  U second{};
};

template <typename T, typename U>
MyPair(T, U) -> MyPair<T, U>;

class Test {
  MyPair p4;   // use of class template 'MyPair' requires template arguments; argument
               // deduction not allowed in non-static class member
};

No exemplo acima, definimos uma classe de template MyPair e usamos CTAD nela. Contudo, quando tentamos usar MyPair como membro não estático da classe Test, por se tratar do contexto de inicialização de membro não estático, o CTAD não funciona, gerando um erro de compilação. Nesse caso, devemos especificar explicitamente os argumentos de template de MyPair.

Podemos ver que, no contexto de inicialização de membros não estáticos, o CTAD não consegue deduzir os argumentos de template. Portanto, quando um template de classe é usado como membro não estático, o desenvolvedor precisa especificar explicitamente todos os argumentos de template. Esse ponto merece atenção especial no desenvolvimento real, para garantir a corretude e a manutenibilidade do código.

Tópicos relacionados

Comentários