Template Metaprogramming - How it Works

Template Metaprogramming - How it Works

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

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.

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

Meta functions 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 meta-function 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 meta-function 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 meta function 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.

No alt text provided for this image

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/meta function 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, Tornike Porchxidze, 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, and Michael Young.

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

?

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

Layton Perrin

Entrepreneur, Leader, Architect, Full-Stack Extreme Virtuoso: Business Analysis, Cyber Security, Data Science. ITIL BPM SLM Expert bringing Modern Approaches to drive Business Processes.

3 年

This is going to be exciting! The power of template metaprogramming is phenomenal :)

回复

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

社区洞察

其他会员也浏览了