Перейти к содержанию

Последнее обновление:2026-10-12

Выведение аргументов шаблона класса CTAD в C++17

Введение

Шаблонное метапрограммирование в C++ давно известно разработчикам: и новички, и опытные программисты могут писать с его помощью обобщённый код с типовыми параметрами. С появлением в C++17 выведения аргументов шаблона класса (CTAD) работа с шаблонами классов стала более наглядной и гибкой.

Весь код этого раздела также будет опубликован в сообществе.

Появление CTAD в C++17 приносит следующие преимущества:

Например, такой громоздкий код

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

мы можем записать просто как:

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

Или ещё пример:

std::tuple<int>

можно упростить до:

std::tuple t1{1};

Итак, давайте сегодня разберёмся с CTAD.

CTAD — полное название Class template argument deduction (CTAD), выведение аргументов шаблона класса: вы даёте компилятору руководство по выведению (deduction guide), и мы можем пользоваться этой возможностью.

Если его не задать, как в примере ниже:

#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;
}

для p2 мы получим ошибку:

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

Как же это исправить?

Достаточно добавить deduction guide, записав его следующим образом.

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

Выведение аргументов шаблона класса (CTAD) упрощает инстанцирование шаблонов классов, позволяя компилятору выводить шаблонные параметры из аргументов конструктора. До появления CTAD разработчики обязаны были явно указывать шаблонные параметры при инстанцировании. С CTAD такая явная спецификация стала необязательной, что делает код более читабельным и простым в сопровождении.

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
}

В приведённом примере CTAD позволяет выводить тип T на основе аргументов конструктора, устраняя необходимость в явном указании типа.

Инициализация нестатических членов

Но есть сценарий, где CTAD не работает, — инициализация нестатических членов. Например, при использовании внешнего MyPair в классе Test его придётся записывать полностью.

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
};

В приведённом примере мы определили шаблонный класс MyPair и применили в нём CTAD. Однако при попытке использовать MyPair в классе Test как нестатический член CTAD не сработает из-за контекста инициализации нестатического члена, что приведёт к ошибке компиляции. В таком случае мы обязаны явно указать шаблонные параметры MyPair.

Мы видим, что в контексте инициализации нестатических членов CTAD не может вывести шаблонные параметры. Поэтому, когда шаблон класса является нестатическим членом, разработчику необходимо явно указывать все шаблонные параметры. На это следует особо обращать внимание в реальной разработке, чтобы обеспечить корректность и сопровождаемость кода.

Похожие темы

Комментарии