Template Arguments

Template Arguments

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

It is quite interesting how the compiler deduces the types for the template arguments. To make it short, you get most of the time the type you expect. The rules do not only apply to function templates (C++98) but also to auto (C++11), to class templates (C++17), and concepts (C++20).

C++ supports function template argument deduction since its beginning. Here is a short recap.

Function Template Argument Deduction

Let me invoke a function template max for int and double

template <typename T>
T max(T lhs, T rhs) {
    return (lhs > rhs)? lhs : rhs;
}

int main() {
  
    max(10, 5);         // (1)
    max(10.5, 5.5);     // (2)
  
}

In this case, the compiler deduces the template arguments from the function arguments. C++ Insights shows that the compiler creates a full specialized function template for max for int (1) and for double (2).

No alt text provided for this image

The process of template type deduction such as in this case produces most of the times the expected type. It is quite enlightening to analyze this process deeper.

Template Type Deduction

When deducing the template type, three entities come into play: T, ParameterType, and expression.

template <typename T>
void func(ParameterType param);

func(expression);

Two types are deduced:

  • T
  • ParameterType

The ParameterType can be a

  • Value
  • Reference (&) or Pointer (*)
  • Univeral Reference (&&)

The expression can be an lvalue or an rvalue having. Additionally, the lvalue or rvalue can be a reference, or const/volatile qualified.

The easiest way to understand the template type deduction process is to vary the ParameterType.

ParameterType is a Value

Taking the parameter by value is probably the most used variant.

template <typename T>
void func(T param);

func(expr);
  • When expr is a reference, the reference is ignored => newExpr is created
  • When newExpr is const or volatile, const or volatile is ignored.

If the ParameterType is a reference or a universal reference, the constness (or volatileness) of expr is respected.

ParameterType is a Reference (&) or Pointer (*)

For simplicity, I use a reference. The analogous argumentation holds for a pointer. Essentially, you exactly get the result you expect.

template <typename T>
void func(T& param);
// void func(T* param);

func(expr);
  • When expr is a reference, the reference is ignored (but added at the end).
  • The expr matches the ParameterType and the resulting type becomes a reference. This means,
  • an expr of type int becomes an int&
  • an expr of type const int becomes a const int&
  • an expr of type const int& becomes a const int&

ParameterType is a Universal Reference (&&)

template <typename T>
void func(T&& param);

func(expr);
  • When expr is an lvalue, the resulting type becomes an lvalue reference.
  • When expr is an rvalue, the resulting type becomes an rvalue reference.

Admittedly, this explanation was pretty technical. Here is an example.

// templateTypeDeduction.cpp

template <typename T>
void funcValue(T param) { }

template <typename T>
void funcReference(T& param) { }

template <typename T>
void funcUniversalReference(T&& param) { }

class RVal{};

int main() {

    const int lVal{};
    const int& ref = lVal;
  
    funcValue(lVal);                  // (1)
    funcValue(ref);
  
    funcReference(lVal);              // (2)
  
    funcUniversalReference(lVal);     // (3)
    funcUniversalReference(RVal());

}

I define and use a function template taking its argument by value (1), by reference (2), and by universal reference (3).

Thanks to C++ Insights, I can visualize the type deduction of the compiler.

  • (1): Both calls of funcValue cause the same instantiation of the function template. The deduced type is an int.
No alt text provided for this image
  • (2): Calling the function funcReference with const int& gives the type const int&.
No alt text provided for this image
  • (3): Using the function funcUniversalReference give an lvalue reference or an rvalue reference.
No alt text provided for this image

There is one interesting fact when you invoke the function funcValue with a C-array. The C-array decays.

Decay of a C-array

Taking a C-array by value is special.

// typeDeductionArray.cpp

template <typename T>
void funcValue(T param) { }

int main() {

    int intArray[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

    funcValue(intArray);
    
}

When you invoke the function template funcValue with a C-array, the C-array decays to a pointer onto its first element. Decay has many facets. It is applied when a function argument is passed by value. Decay means that an implicit conversion function-to-pointer, array-to-pointer, or lvalue-to-rvalue is applied. Additionally, the reference of a type T and its const-volatile qualifiers are removed.

Here is the screenshot of the program from C++ Insights.

No alt text provided for this image

This essentially means that you don't know the size of the C-array. 

But there is a trick. Taking the C-array by reference and pattern matching on the type and the size on the C-array gives you the size of the C-array:

// typeDeductionArraySize.cpp

#include <cstddef>
#include <iostream>

template <typename T, std::size_t N>
std::size_t funcArraySize(T (&arr)[N]) { 
    return N;
}

int main() {

    std::cout << '\n';

    int intArray[10]{ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9};

    funcArraySize(intArray);

    std::cout << "funcArraySize(intArray): " << funcArraySize(intArray) << '\n';

    std::cout << '\n';
    
}

The function template funcArraySize deduces the size of the C-arrays. I gave for readability reasons the C-array parameter the name arr: std::size_t funcArraySize(T (&arr)[N]). This is not necessary and you can just write std::size_t funcArraySize(T (&)[N]). Here are the internals from C++ Insights.

No alt text provided for this image

 Finally, the output of the program:

No alt text provided for this image

When you understand template type deduction, you essentially understand auto type deduction in C++11.

auto Type Deduction

auto type deduction uses the rules of template type deduction.

To remind you, these are the essential entities of template type deduction:

template <typename T> 
void func(ParameterType param);

auto val = 2011;

Understanding auto means, that you have to regard auto as the replacements for T and the type specifiers of auto as the replacements for the ParameterType in the function template. 

The type specifier can be a value (1), a reference (2), or a universal reference (3).

auto val = arg;      // (1)

auto& val = arg;     // (2)

auto&& val = arg;    // (3)

Let's try it out and change the previous program templateTypeDeduction.cpp and use auto instead of function templates.

// autoTypeDeduction.cpp

class RVal{};

int main() {

    const int lVal{};
    const int& ref = lVal;
  
    auto val1 = lVal;          // (1)
    auto val2 = ref;
  
    auto& val3 = lVal;         // (2)
  
    auto&& val4 = lVal;        // (3)
    auto&& val5 = RVal();

}

When you study the resulting types in C++ Insights, you see that they are identical to the types deduced in the program templateTypeDeduction.cpp.

No alt text provided for this image

Of course, auto also decays when it takes a C-array by value.

The New pdf-Bundle is Ready: C++20 Coroutines

I have prepared the pdf-bundle. To get it is quite simple. If you subscribe to my German or English newsletter, you will get the link to the pdf bundle.

Here is more information about the pdf-Bundle:  C++ Coroutines.

No alt text provided for this image

What's next?

C++17 makes type deduction more powerful. First, automatic type deduction is possible for non-type template parameters, and second, class templates can also deduce their arguments. In particular, class template argument deduction makes the life of a programmer much easier.

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++


 

 

要查看或添加评论,请登录

Rainer Grimm的更多文章

  • std::execution: Asynchronous Algorithms

    std::execution: Asynchronous Algorithms

    supports many asynchronous algorithms for various workflows. Presenting proposal P2300R10 is not easy.

  • My ALS Journey (17/n): Christmas Special

    My ALS Journey (17/n): Christmas Special

    Today, I have a special Christmas gift. My ALS Journey so far Make the Difference Let’s do something great together:…

  • std::execution

    std::execution

    std::execution, previously known as executors or Senders/Receivers, provides “a Standard C++ framework for managing…

    1 条评论
  • C++26 Core Language: Small Improvements

    C++26 Core Language: Small Improvements

    There are more small improvements in the C++26 language, which you should know. static_assert extension First, here’s…

  • My ALS Journey (16/n): Good Bye Training / Hello Mentoring

    My ALS Journey (16/n): Good Bye Training / Hello Mentoring

    In 2025, I will no longer offer C++ classes. Instead, I will only offer C++ mentoring in the future.

    1 条评论
  • Placeholders and Extended Character Set

    Placeholders and Extended Character Set

    Placeholders are a nice way to highlight variables that are no longer needed. Additionally, the character set of C++26…

    4 条评论
  • Contracts in C++26

    Contracts in C++26

    Contracts allow you to specify preconditions, postconditions, and invariants for functions. Contracts should already be…

    5 条评论
  • Mentoring as a Key to Success

    Mentoring as a Key to Success

    I know that we are going through very challenging times. Saving is the top priority.

  • Reflection in C++26: Determine the Layout

    Reflection in C++26: Determine the Layout

    Thanks to reflection, you can determine the layout of types. My examples are based on the reflection proposal P2996R5.

    5 条评论
  • My ALS Journey (15/n): A typical Day

    My ALS Journey (15/n): A typical Day

    You may wonder how my day looks. Let me compare a day before ALS with a current day.

    3 条评论

社区洞察

其他会员也浏览了