Class Template Argument Deduction
Develop a thorough understanding of class template argument deduction.
Instantiating a class template before C++17
Before C++17, template argument deduction only worked for functions but not classes. This meant that when a class template had to be instantiated, all the template arguments had to be supplied. The following snippet shows several examples:
template<typename T>struct wrapper{T data;};std::pair<int, double> p{ 42, 42.0 };std::vector<int> v{ 1,2,3,4,5 };wrapper<int> w{ 42 };
By leveraging template argument deduction for function templates, some standard types feature helper functions that create an instance of the type without the need to explicitly specify template arguments. Such examples are std::make_pair
for std::pair
and std::make_unique
for std::unique_ptr
. These helper function templates, used in corroboration with the auto
keyword, avoid the need for specifying template arguments for class templates. Here’s an example:
auto p = std::make_pair(42, 42.0);
Although not all standard class templates have such a helper function for creating instances, it’s not hard to write our own. In the following snippet, we can see a make_vector
function template used to create a std::vector<T>
instance and a make_wrapper
function template to create a wrapper<T>
instance:
template<typename T, typename... Ts, typename Allocator = std::allocator<T>>auto make_vector(T&& first, Ts&&... args){return std::vector<std::decay_t<T>, Allocator> {std::forward<T>(first),std::forward<Ts>(args)...};}template<typename T>constexpr wrapper<T> make_wrapper(T&& data){return wrapper<T> { data }; // Template argument `<T>` is added by the TCE// as this syntax was added in C++17 and above}auto v = make_vector(1, 2, 3, 4, 5);auto w = make_wrapper(42);
Class template argument deduction after C++17
The C++17 standard has simplified the use of class templates by providing template argument deduction for them. Therefore, as of C++17, the first snippet shown in this lesson can be simplified as follows:
std::pair p{ 42, 42.0 }; // std::pair<int, double>std::vector v{ 1,2,3,4,5 }; // std::vector<int>wrapper w{ 42 }; // wrapper<int>
This is possible because the compiler is able to deduce the template arguments from the type of the initializers. In this example, the compiler deduced it from the initializing expression of the variables. But the compiler is also able to deduce template arguments from new
expressions and function-style cast expressions. These are exemplified next:
template<typename T>struct point_t{point_t(T vx, T vy) : x(vx), y(vy) {}private:T x;T y;};auto p = new point_t(1, 2); // [1] point<int>// new expressionstd::mutex mt;auto l = std::lock_guard(mt); // [2] std::lock_guard<std::mutex>// function-style cast expression
The way template argument deduction works for class templates is different than for function templates, but it relies on the latter. When encountering the name of a class template in a ...