Template Argument Deduction of Class Templates

Template Argument Deduction of Class Templates

This post is a cross-post from www.ModernesCpp.com.

In my last post Template Arguments, I wrote about function template type deduction (C++98) and auto type deduction (C++11). Today I wear more modern hats. I start with automatic type deduction of non-type template parameters and class templates (C++17) and finish with automatic type deduction of concepts (C++20).

Following the chronological order, let me start with two C++17 features: type deduction of non-type template parameters and type deduction of class templates in C++17.

Automatic type deduction of non-type template parameters

First of all. What are non-type template parameters? These are nullptr, integral values such as bool, and int, lvalue references, pointer, enumerations, and with C++20 floating-point values. Most of the time, integral types are used and so do I.

After this theory, let's start with an example.

template <auto N>          // (1)
class MyClass{
    ....
};

template <int N>          // (2)
class MyClass<N> {
    ....
};


MyClass<'x'> myClass1;    // (3)
MyClass<2017>  myClass2;  // (4)

By using auto in (1) in the template signature, N is a non-type template parameter. The compiler will automatically deduce it. You can also partially specialize for int (2). The template instantiation (3) will use the primary template (1) and the following template instantiation the partial specialization for int (4).

The usual type modifiers can be used to constrain the type of the non-type template parameters.

template <const auto* p> 
class S;

In this declaration of a class template S, p must be a pointer to const.

The automatic type deduction for non-type templates can also be applied to variadic templates.

template <auto... ns> 
class VariadicTemplate{ .... }; 

template <auto n1, decltype(n1)... ns>
class TypedVariadicTemplate{ .... };

VariadicTemplate can deduce an arbitrary number of non-type template parameters. TypeVariadicTemplate will only deduce the first template parameter. The remaining templated parameters will be of the same type such as the first type: decltype(n1).

Automatic type deduction from class templates makes the usage class template quite comfortable.

Automatic Type Deduction of Class Templates

A function template can deduce its type parameters from its function arguments. But that was not possible for special functions: constructors of class templates. With C++17, this statement is simply wrong. A constructor can deduce its type parameters from its constructor arguments. Here is a first example.

// templateArgumentDeduction.cpp

#include <iostream>

template <typename T>
void showMe(const T& t) {
    std::cout << t << '\n';
}

template <typename T>
struct ShowMe{
    ShowMe(const T& t) {
        std::cout << t << '\n';
    }
};

int main() {
  
    std::cout << '\n';
    
    showMe(5.5);          // not showMe<double>(5.5);
    showMe(5);            // not showMe<int>(5);
    
    ShowMe(5.5);          // not ShowMe<double>(5.5);
    ShowMe(5);            // not ShowMe<int>(5);
  
    std::cout << '\n';
    
}

Let me say a few words about the main function. The instantiation of the function template showMe is valid since the first C++ standard C++98, but the instantiation of the class template ShowMe since C++17. From the user's perspective, the usage of functions template or class templates feels just like an ordinary function or class.

No alt text provided for this image

Maybe, you are not convinced. Here are more examples of class template argument deduction.

// classTemplateArgumentDeduction.cpp

#include <array>
#include <vector>
#include <mutex>
#include <memory>

int main() {
  
    std::array myArr{1, 2, 3};          // deduces std::array<int, 3> 
    std::vector myVec{1.5, 2.5};        // deduces std::vector<double>
 
    std::mutex mut;
    std::lock_guard myLock(mut);       // deduces std::lock_guard<mutex>(mut);
  
    std::pair myPair(5, 5.5);          // deduces std::pair<int, double>
    std::tuple myTup(5, myArr, myVec); // deduces std::tuple<int, 
                                       // std::array<int, 3>, std::vector<double>>
}

The comments show the by the C++17 compiler deduces type. Thanks to C++ Insights, you can visualize this process of template argument deduction.

The last two examples to std::pair and std::tuple are pretty interesting. Before C++17, we used factory functions such as std::make_pair or std::make_tuple to create a std::pair or a std::tuple without specifying the type parameters. In contrast to class templates, the compiler could deduce the type parameter from the function arguments. Here is a simplified version of std::pair.

// makePair.cpp

#include <utility>

template<typename T1, typename T2>
std::pair<T1, T2> make_pair2(T1 t1, T2 t2) { 
    return std::pair<T1, T2>(t1, t2); 
}

int main() {
	
   auto arg{5.5};
   auto pair1 = std::make_pair(5, arg);
   auto pair2 = make_pair2(5, arg);
   auto pair3 = std::pair(5, arg);
   
}

The compiler deduces the same type for pair1 and pair2. With C++17, we don't need this factory function anymore and can directly invoke the constructor of std::pair to get pair3.

No alt text provided for this image

You can study the program on C++ Insights.

You may wonder that my function template make_pair2 took its arguments by value. std::make_pair decays its arguments and so does my function template make_pair2. I wrote about the decay of function arguments in my last post Template Arguments.

Before I write a few words about the automatic type deduction with concepts, I want to emphasize is explicit. Automatic type deduction is more than convenient. It's a security feature. When you don't specify the type, you cannot make an error.

// automaticTypeDeduction.cpp

#include <string>

template<typename T>
void func(T) {};

template <typename T>
struct Class{
  Class(T){}
};

int main() {
  
    int a1 = 5.5;              // static_cast<int>(5.5)
    auto a2 = 5.5;
  
    func<float>(5.5);          // static_cast<float>(5.5)
    func(5.5);
  
    Class<std::string> class1("class"); // calls essentially std::string("class")
    Class class2("class");
  
}

All errors are only due to the fact that I explicitly specified the type:

  • int a1 triggers the narrowing conversion from double to int
  • func<float>(5.5) causes the conversion from the double value 5.5 to float
  • Class<std::string> class1("class") creates a C++-string initialized with a C-string.

If you want to study the program, here it is: C++ Insights.

There is not much to add to the story of automatic type deduction when concepts come into play.

Automatic Type Deduction with Concepts

Automatic type deduction with concepts work like expected:

// typeDeductionConcepts.cpp

#include <concepts>

void foo(auto t) {}                  // (1)

void bar(std::integral auto t){}     // (2)

template <std::regular T>            // (3)
struct Class{
  Class(T){}
};

int main() {

    foo(5.5);
    bar(5);
    Class cl(true);

} 

Whether you use an unconstrained placeholder ( auto in line 1), a constrained placeholder (concept in line 2), or a restricted template parameter (concept in line 3), the compiler deduces the expected type. C++ Insights helps to visualize the type deduction.

No alt text provided for this image

What's next?

In my next post, I write about the next exciting feature of templates: specialization. You can fully specialize a function template or class template. Additionally, a class template can be partially specialized.


Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, Marko, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, espkk, Louis St-Amour, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Neil Wang, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Tobi Heideman, Daniel Hufschläger, Red Trip, Alexander Schwarz, Tornike Porchxidze, Alessandro Pezzato, Evangelos Denaxas, Bob Perry, Satish Vangipuram, and Andi Ireland.

Thanks in particular to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, and Said Mert Turkal.

My special thanks to Embarcadero

Seminars

I'm happy to give online-seminars or face-to-face seminars world-wide. Please call me if you have any questions.

Bookable (Online)

German

Standard Seminars (English/German)

Here is a compilation of my standard seminars. These seminars are only meant to give you a first orientation.

New

Contact Me

Modernes C++


 


Thank you for your post and all of your hard work, Rainer!

Like
Reply

To view or add a comment, sign in

More articles by Rainer Grimm

  • Charity run for ALS

    Tomorrow, on the 28th, there will be a charity run for ALS in Rottenburg. The course is exactly 1 km long.

    2 Comments
  • Small Safety Improvements in the C++ 26 Core Language

    Safety is an important concern in C++26. Contracts are probably the most important feature for safety.

    1 Comment
  • My ALS Journey (30/n): Cippi at the CppCon

    This week was very exciting for Cippi. She visited CppCon in Aurora, near Denver.

    2 Comments
  • Contracts: Evaluation Semantic

    After briefly presenting the details of contracts in my last article, “Contracts: A Deep Dive“, I would like to take a…

  • My ALS Journey (29/n): I feel Good

    I often receive messages asking about my health and wishing me well. I am very happy to receive these messages and just…

    5 Comments
  • Contracts: A Deep Dive

    August 25, 2025/in C++26/by Rainer GrimmI already introduced contracts in the article “Contracts in C++26”. In this…

  • My ALS Journey (28/n): Bureaucracy – The German Disease

    Today I want to write about a sad topic. Bureaucracy in the German healthcare system is becoming increasingly absurd.

    2 Comments
  • Data-Parallel Types: Algorithms

    The data-parallel types library has four special algorithms for SIMD vectors. The four special algorithms are min, max,…

  • My ALS Journey (27/n): An Emergency Call

    Firstly, I would like to say that I am doing very well and have made a full recovery from my incident. However, I would…

    8 Comments
  • Data-Parallel Types: Reduction

    In this article, I will discuss reduction and mask reduction for data-parallel types. Reduction A reduction reduces the…

Others also viewed

Explore content categories