Namespaces
Variants
Views
Actions

Class template deduction(since C++17)

From cppreference.com
< cpp‎ | language

In order to instantiate a class template, every template argument must be known, but not every template argument has to be specified. In the following contexts the compiler will deduce the missing template arguments from the type of the initializer:

  • any declaration that specifies initialization of a variable and variable template
std::pair p(2, 4.5);     // deduces to std::pair<int, double> p(2, 4.5);
std::tuple t(4, 3, 2.5); // same as auto t = std::make_tuple(4, 3, 2.5);
std::less l;             // same as std::less<void> l;
template<class T> struct A { A(T,T); };
auto y = new A{1,2}; // allocated type is A<int>
auto lck = std::lock_guard(mtx); // deduces to std::lock_guard<std::mutex>
std::copy_n(vi1, 3, std::back_insert_iterator(vi2)); // or std::back_inserter(vi2)
std::for_each(vi.begin(), vi.end(), Foo([&](int i) {...})); // deduces Foo<T>, where T 
                                                            // is the unique lambda type

[edit] Automatic deduction guides

When a function-style cast or declaration of a variable uses the name of a class template without an argument list as the type specifier, deduction will proceed as follows:

  • for each constructor (or constructor template) Ci declared in the named primary template (if it is defined), a fictional function template Fi, is constructed, such that
  • template parameters of Fi are the template parameters of the named class template followed (if Ci is a constructor template) by the template parameters of Ci (default template arguments are included too)
  • the function parameters of Fi are the constructor parameters
  • the return type of Fi is the template name followed by the template parameters of the class template enclosed in <>
  • If the primary class template C is not defined or does not declare any constructors, an additional fictional function template is added, derived as above from a hypothetical constructor C()
  • In any case, an additional fictional function template derived as above from a hypothetical constructor C(C) is added, called the copy deduction candidate.
  • template argument deduction and overload resolution is then performed for initialization of a fictional object of hypothetical class type, whose constructor signatures match the guides (except for return type) for the purpose of forming an overload set, and the initializer is provided by the context in which class template argument deduction was performed. These fictional constructors are public members of the hypothetical class type. They are explicit if the guide was formed from an explicit constructor. If overload resolution fails, the program is ill-formed. Otherwise, the return type of the selected F template specialization becomes the deduced class template specialization.
template<class T> struct UniquePtr { UniquePtr(T* t); };
UniquePtr dp{new auto(2.0)};
// One declared constructor:
// C1: UniquePtr(T*);
// Set of automatic deduction guides:
// F1: template<class T> UniquePtr<T> F(T *p);
// F2: template<class T> UniquePtr<T> F(UniquePtr<T>); // copy deduction candidate
// imaginary class to initialize:
// struct X {
//     template<class T> X(T *p);          // from F1
//     template<class T> X(UniquePtr<T>);  // from F2
// };
// direct-init of an X object with "new double(2.0)" as the initializer
// selects the constructor that corresponds to the guide F1 with T = double
// For F1 with T=double, the return type is UniquePtr<double>
// result:
// UniquePtr<double> dp{new auto(2.0)}

Or, for a more complex example (note: "S::N" would not compile: scope resolution qualifiers are not something that can be deduced):

template<class T> struct S {
  template<class U> struct N {
    N(T);
    N(T, U);
    template<class V> N(V, U);
  };
};
 
S<int>::N x{2.0, 1};
// the automatic deduction guides are (note that T is already known to be int)
// F1: template<class U> S<int>::N<U> F(int);
// F2: template<class U> S<int>::N<U> F(int, U);
// F3: template<class U, class V> S<int>::N<U> F(V, U);
// F4: template<class U> S<int>::N<U> F(S<int>::N<U>); (copy deduction candidate)
// Overload resolution for direct-list-init with "{2.0, 1}" as the initializer
// chooses F3 with U=int and V=double.
// The return type is S<int>::N<int>
// result:
// S<int>::N<int> x{2.0, 1};

[edit] User-defined deduction guides

The syntax of a user-defined deduction guide is the syntax of a function declaration with a trailing return type, except that it uses the name of a class template as the function name:

explicit(optional) template-name ( parameter-declaration-clause ) -> simple-template-id ;

User-defined deduction guides must name a class template and must be introduced within the same semantic scope of the class template (which could be namespace or enclosing class) and, for a member class template, must have the same access, but deduction guides do not become members of that scope.

A deduction guide is not a function and does not have a body. Deduction guides are not found by name lookup and do not participate in overload resolution except for the overload resolution against other deduction guides when deducting class template arguments. Deduction guides cannot be redeclared in the same translation unit for the same class template.

// declaration of the template
template<class T> struct container {
    container(T t) {}
    template<class Iter> container(Iter beg, Iter end);
};
// additional deduction guide
template<class Iter>
container(Iter b, Iter e) -> container<typename std::iterator_traits<Iter>::value_type>;
// uses
container c(7); // OK: deduces T=int using an automatic guide
std::vector<double> v = { /* ... */};
auto d = container(v.begin(), v.end()); // OK: deduces T=double
container e{5, 6}; // Error: there is no std::iterator_traits<int>::value_type

The fictional constructors for the purpose of overload resolution (described above) are explicit if they correspond to an automatic deduction guide formed from an explicit constructor or to a user-defined deduction guide that uses the keyword explicit. As always, such constructors are ignored in copy-initialization context:

template<class T> struct A {
    explicit A(const T&, ...) noexcept; // #1
    A(T&&, ...);                        // #2
};
 
int i;
A a1 = { i, i }; // error: cannot deduce from rvalue reference in #2,
                 // and #1 is explicit, and not considered in copy-initialization.
A a2{i, i};      // OK, #1 deduces to A<int> and also initializes
A a3{0, i};      // OK, #2 deduces to A<int> and also initializes
A a4 = {0, i};   // OK, #2 deduces to A<int> and also initializes
 
template<class T> A(const T&, const T&) -> A<T&>; // #3
template<class T> explicit A(T&&, T&&)  -> A<T>;  // #4
 
A a5 = {0, 1};   // error: #3 deduces to A<int&>
                     // and #1 & #2 result in same parameter constructors.
A a6{0,1};       // OK, #4 deduces to A<int> and #2 initializes
A a7 = {0, i};   // error: #3 deduces to A<int&>
A a8{0,i};       // error: #3 deduces to A<int&>

Using a member typedef or alias template in a constructor or constructor template's parameter list does not, by itself, render the corresponding parameter of the implicitly generated guide a non-deduced context.

template<class T> struct B {
    template<class U> using TA = T;
    template<class U> B(U, TA<U>);  //#1
};
 
// Implicit deduction guide generated from #1 is the equivalent of
// template<class T, class U> B(U, T) -> B<T>;
// rather than
// template<class T, class U> B(U, typename B<T>::template TA<U>) -> B<T>;
// which would not have been deducible
 
B b{(int*)0, (char*)0}; // OK, deduces B<char*>

[edit] Notes

Class template deduction is only performed if no template arguments are provided. If at least one argument is specified, deduction does not take place.

std::tuple t(1, 2, 3);              // OK: deduction
std::tuple<int,int,int> t(1, 2, 3); // OK: all arguments are provided
std::tuple<int> t(1, 2, 3);         // Error: partial deduction

Class template deduction of aggregates typically requires deduction guides:

template<class A, class B> struct Agg {A a; B b; };
// automatic guides are formed from default, copy, and move constructors
template<class A, class B> Agg(A a, B b) -> Agg<A, B>;
Agg agg{1, 2.0}; // deduced to Agg<int, double> from the user-defined guide
 
template <class... T>
array(T&&... t) -> array<std::common_type_t<T...>, sizeof...(T)>;
auto a = array{1, 2, 3u}; // deduced from the user-defined guide

User-defined deduction guides do not have to be templates:

template<class T> struct S { S(T); };
S(char const*) -> S<std::string>;
S s{"hello"}; // deduced to S<std::string>

Within the scope of a class template, the name of the template without a parameter list is an injected class name, and can be used as a type. In that case, class argument deduction does not happen and template parameters must be supplied explicitly:

template<class T>
struct X {
  template<class Iter> X(Iter b, Iter e) { }
 
  template<class Iter>
  auto foo(Iter b, Iter e) { 
     return X(b, e); // no deduction: X is the current X<T>
  }
  template<class Iter>
  auto bar(Iter b, Iter e) { 
     return X<Iter::value_type>(b, e); // must specify what we want
  }
};

In overload resolution, partial ordering takes precedence over whether a function template is generated from a guide: if the function template generated from the constructor is more specialized than the one generated from the deduction guide, the one generated from the constructor is chosen.

template<class T> struct A {
    A(T, int*);     // #1
    A(A<T>&, int*); // #2
    enum { value };
};
template<class T, int N = T::value> A(T&&, int*) -> A<T>; //#3
 
A a{1,0}; // uses #1 to deduce A<int> and initializes with #1
A b{a,0}; // uses #2 (more specialized than #3) to deduce A<int> and initializes with #2

When earlier tiebreakers, including partial ordering, failed to distinguish between two candidate function templates, the following rules apply:

  • A function template generated from a guide is preferred over one implicitly generated from a constructor or constructor template.
  • The copy deduction candidate is preferred over all other function templates implicitly generated from a constructor or constructor template.
  • A function template implicitly generated from a non-template constructor is preferred over a function template implicitly generated from a constructor template.
template <class T> struct A {
    using value_type = T;
    A(value_type); // #1
    A(const A&); // #2
    A(T, T, int); // #3
    template<class U> 
    A(int, T, U); // #4
}; // A(A); #5, the copy deduction candidate
 
A x (1, 2, 3); // uses #3, generated from a non-template constructor
 
template <class T> A(T) -> A<T>;  // #6, less specialized than #5
 
A a (42); // uses #6 to deduce A<int> and #1 to initialize
A b = a;  // uses #5 to deduce A<int> and #2 to initialize
 
template <class T> A(A<T>) -> A<A<T>>;  // #7, as specialized as #5
 
A b2 = a;  // uses #7 to deduce A<A<int>> and #1 to initialize

An rvalue reference to a cv-unqualified template parameter is not a forwarding reference if that parameter is a class template parameter:

template<class T> struct A {
    template<class U>
    A(T&&, U&&, int*);   // #1: T&& is not a forwarding reference
                         //     U&& is a forwarding reference
    A(T&&, int*); // #2: T&& is not a forwarding reference
};
 
template<class T> A(T&&, int*) -> A<T>; // #3: T&& is a forwarding reference
 
int i, *ip;
A a{i, 0, ip};  // error, cannot deduce from #1
A a0{0, 0, ip}; // uses #1 to deduce A<int> and #1 to initialize
A a2{i, ip};    // uses #3 to deduce A<int&> and #2 to initialize