Template Metaprogramming - How it Works

Contents[Show]

In my last post "Template Metaprogramming - How it All Started", I wrote about the roots of template metaprogramming and presented the hello world of template metaprogramming: calculating the factorial of a number at compile time. In this post, I will write about, how template metaprogramming can be used to modify types at compile time.

 

TemplateMetaprogramming The factorial program in the last post "Template Metaprogramming - How it All Started" was a nice example but not idiomatic for template metaprogramming. Manipulating types at compile time is typical in template metaprogramming.

Type Manipulation at Compile Time

For example, here is what std::move is conceptionally doing:

static_cast<std::remove_reference<decltype(arg)>::type&&>(arg);

 

std::move takes its argument arg, deduces its type (decltype(arg)), removes its reference (std::remove_reverence), and casts it to an rvalue reference (static_cast<...>::type&&>). Essentially,
std::move is an rvalue reference cast. Now, move semantics can kick in.

How can a function remove constness from its argument?

// removeConst.cpp

#include <iostream>
#include <type_traits>

template<typename T >
    struct removeConst {
    using type = T;        // (1)
};

template<typename T >
    struct removeConst<const T> {
    using type = T;       // (2)
};

int main() {

    std::cout << std::boolalpha;
    std::cout << std::is_same<int, removeConst<int>::type>::value << '\n';       // true    
    std::cout << std::is_same<int, removeConst<const int>::type>::value << '\n'; // true

}

 

I implemented removeConst the way std::remove_const is probably implemented in the type-traits library. std::is_same from the type-traits library helps me to decide at compile-time if both types are the same. In case of removeConst<int> the primary or general class template kicks in; in case of removeConst<const int>, the partial specialization for const T applies. The critical observation is that both class templates return the underlying type in (1) and (2) via the alias type. As promised, the constness of the argument is removed.


There are additional observations:

  • Template specialization (partial or full) is conditional execution at compile-time. Let me be more specific: When I use removeConst with a non-constant int, the compiler chooses the primary or general template. When I use a constant int, the compiler chooses the partial specialization for const T.
  • The expression using type = T serves as the return value, which is, in this case, a type.
  • When you study the program removeConst.cpp on C++ Insights, you see that the expression that the expression std::is_same<int, removeConst<int>::type>::value boils down to the boolean value std::integral_constant<bool, true>::value that is displayed as true.

Let me step back and write about template metaprogramming for a more conceptual view.

More Meta

At run time, we use data and functions. At compile time we use metadata and metafunctions. Quite logically, it's called meta because we do metaprogramming.

Metadata

Metadata are values that metafunctions us at compile time.

There are three types of values:

  • Types such as int, or double
  • Non-types such as integrals, enumerators, pointers, references, floating-points with C++20
  • Templates such as std::vector, or std::deque

You can read more about the three types of values in my previous post "Alias Templates and Template Parameters".

Metafunctions

Metafunctions are functions that are executed at compile time.

Admittedly, this sounds strange: Types are used in template metaprogramming to simulate functions. Based on the definition of metafunctions, constexpr functions that can be executed at compile time, are also metafunctions. The same holds for consteval functions in C++20.

Here are two metafunctions.

 

template <int a , int b>
struct Product {
    static int const value = a * b;
};

template<typename T >
struct removeConst<const T> {
    using type = T;
};

 

The first metafunction Product returns a value and the second one removeConst returns a type. The name value and type are just naming conventions for the return values. If a meta-function returns a value, it is called value; if it returns a type, it is called type. The type-traits library follows exactly this naming convention.

It is quite enlightening to compare functions with metafunctions.

Functions versus Metafunctions

The following function power and the metafunction Power calculate pow(2, 10) at run time and compile time.

 

// power.cpp

#include <iostream>

int power(int m, int n) {                               
    int r = 1;
    for(int k = 1; k <= n; ++k) r *= m;
    return r;                                        
}

template<int m, int n>                              
struct Power {
    static int const value = m * Power<m, n-1>::value;
};
                          
template<int m>                                     
struct Power<m, 0> {                                   
    static int const value = 1;                       
};

int main() {
	
    std::cout << '\n';	
	
    std::cout << "power(2, 10)= " << power(2, 10) << '\n';
    std::cout << "Power<2,10>::value= " << Power<2, 10>::value << '\n';
	
    std::cout << '\n';
}

 

This is the main difference:

  • Arguments: The function arguments go into the round brackets (( ... )) and the metafunction arguments go into the sharp brackets (< ...>). This observation also holds for the definition of the function and the metafunction. The function uses round brackets and the metafunction sharp brackets. Each metafunction argument produces a new type.
  • Return value: The function uses a return statement, and the metafunction a static integral constant value.

I elaborate more on this comparison in the upcoming post about constexpr and consteval functions. Here is the output of the program.

 power

power is executed at run time and Power at compile time, but what is happening in the following example?

 

// powerHybrid.cpp

#include <iostream>

template<int n>
int Power(int m){
    return m * Power<n-1>(m);
}

template<>
int Power<0>(int m){
    return 1;
}

int main() {
    
    std::cout << '\n';

    std::cout << "Power<0>(10): " << Power<0>(20) << '\n';
    std::cout << "Power<1>(10): " << Power<1>(10) << '\n';
    std::cout << "Power<2>(10): " << Power<2>(10) << '\n';
    

    std::cout << '\n';

}

 

The question is obviously: Is Power a function or a metafunction? I promise, the answer to this question gives you more insight.

What's next?

In my next post, I analyze the function/metafunction Power and introduce the type-traits library. The type traits library is idiomatic for compile-time programming in C++.

 

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, Alessandro Pezzato, Evangelos Denaxas, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Dimitrov Tsvetomir, Leo Goodstadt, Eduardo Velasquez, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, and Holger Detering.

 

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

 

 

My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small

 

My special thanks to PVS-Studio PVC Logo

 

Seminars

I'm happy to give online seminars or face-to-face seminars worldwide. 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++,

RainerGrimmSmall

 
 

 

 

My Newest E-Books

Course: Modern C++ Concurrency in Practice

Course: C++ Standard Library including C++14 & C++17

Course: Embedded Programming with Modern C++

Course: Generic Programming (Templates)

Course: C++ Fundamentals for Professionals

Interactive Course: The All-in-One Guide to C++20

Subscribe to the newsletter (+ pdf bundle)

Blog archive

Source Code

Visitors

Today 915

Yesterday 8760

Week 9675

Month 205832

All 7691224

Currently are 147 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments