W3cubDocs

/C++

Class template deduction(since C++17)

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

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

Implicit deduction guides

When a function-style cast or declaration of a variable using a direct parenthesized or braced initializer 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 template, a fictional function template Fi, known as implicit deduction guide, 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 <>
  • template argument deduction and overload resolution is then performed for an invented call to F with the parenthesized or braced expressions used as arguments. If that call doesn't yield a "best viable function", the program is ill-formed. Otherwise, the return type of the selected F template specialization becomes the deduced class template specialization.

When using a declaration of a variable using the copy-initialization syntax, the rules are the same but explicit constructors and constructor templates are ignored, and the entire initializer expression is the single call argument during the deduction process.

template<class T> struct UniquePtr { UniquePtr(T* t); };
UniquePtr dp{new auto(2.0)};
// Set of constructors:
// C1: UniquePtr(T*)
// C2: UniquePtr(const UniquePtr&)
// C3: UniquePtr(UniquePtr&&)
// Set of implicit deduction guides:
// F1: template<class T> UniquePtr<T> F(T *p);
// F2: template<class T> UniquePtr<T> F(UniquePtr<T> const&);
// F3: template<class T> UniquePtr<T> F(UniquePtr<T> &&);
// overload resolution for the call "F(new double(2.0))" selects F1
// and deduces its return type as 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<classV> N(V, U);
  };
};
 
S<int>::N x{2.0, 1};
// the implicit deduction guides are (note that T is already known to be int)
// F1: template<class U> S<int>::N<U> F(S<int>::N<U> const&);
// F2: template<class U> S<int>::N<U> F(S<int>::N<U> &&);
// F3: template<class U> S<int>::N<U> F(int);
// F4: template<class U> S<int>::N<U> F(int, U);
// F5: template<class U, class V> S<int>::N<U> F(V, U);
// Overload resolution for the call "F(2.0, 1)" chooses F5 with U=int and V=double
// the return type is S<int>::N<int>
// result:
// S<int>::N<int> x{2.0, 1};

Explicit deduction guides

The syntax of an explicit 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:

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

Explicit 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), but they 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.

// 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 implicit 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

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: explicit
std::tuple<int> t(1, 2, 3); // Error: partial deduction

Class template deduction of aggregates typically requires explicit deduction guides:

template<class A, class B> struct Agg {A a; B b; };
// implicit 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 explicit guide
 
template <class... T>
array(T&&... t) -> array<std::common_type_t<T...>, sizeof...(T)>;
auto a = array{1, 2, 3u}; // deduced from the explicit guide

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


Implicit guides formed from converting move constructors may interpret rvalue references as universal references. When this is incorrect, an explicit guide may be necessary:

template<class T>
struct Wrapper
{
    T value;
    Wrapper(T const& x): value(x) {}
    Wrapper(T&& y): value(std::move(x)) {}
};
// replace the guide formed by the converting move constructor:
template<class T> Wrapper(T&& y) -> Wrapper<std::remove_reference_t<T>>;
int main()
{
    std::string foo = "Hello";
    auto w = Wrapper(foo); // Compilation error without the explicit guide above
}

© cppreference.com
Licensed under the Creative Commons Attribution-ShareAlike Unported License v3.0.
http://en.cppreference.com/w/cpp/language/class_template_deduction