C++ Core Guidelines: Rules for Variadic Templates


Variadic templates are a typical feature of C++: from the users perspective they are easy to use, but from the implementor's perspective they look quite scary. Today's post is mainly about the implementor's perspective.


theater 311730 1280

Before I write about the details to variadic temples, I want to make a short remark about my introduction to this post. I often wear two heads, when I teach C++: one for the user and one for the implementor. Features such as templates are easy to use but challenging to implement. This significant gap is typically for C++ and I assume deeper than in other mainstream programming languages such as Python, Java, or even C. Honestly, I have no problem with this gap. I call this gap abstraction, and it is an essential part of the power of C++. The art of the implementer of the library or framework is it to provide easy to use (difficult to misuse) and stable interfaces.  If you don't get the point, wait for the next section, when I develop std::make_unique.

Today's post is based on three rules:

You can already guess it. The three rules are title-only; therefore, I make one story out of the first three rules.

As promised, I want to develop std::make_unique. std::make_unique is a function template that returns a dynamically allocated object, protected by a std::unique_ptr. Let me show you a few use-cases.

// makeUnique.cpp

#include <memory>

struct MyType{
    MyType(int, double, bool){};

int main(){
    int lvalue{2020};
    std::unique_ptr<int> uniqZero = std::make_unique<int>();      // (1)
    auto uniqEleven = std::make_unique<int>(2011);                // (2)
    auto uniqTwenty = std::make_unique<int>(lvalue);              // (3)
    auto uniqType = std::make_unique<MyType>(lvalue, 3.14, true); // (4)


Based on this use-case, what are the requirements of std::make_unique?

  1. It should deal with an arbitrary number of arguments. The std::make_unique calls gets 0, 1, and 3 arguments.
  2. It should deal with lvalues and rvalues. The std::make_unique call in line (2) gets an rvalue and in line (3) an lvalue. The last one even gets an rvalue und an lvalue.
  3. It should forward it arguments unchanged to the underlying constructor. This means, the constructor of std::unique_ptr should get an lvalue/rvalue if std::make_unique gets an lvalue/rvalue.

This requirements are typically for factory functions such as std::make_unique, std::make_shared, std::make_tuple, but also std::thread. Both rely on two powerful features of C++11:

  1. Variadic templates
  2. Perfect forwarding

Now, I want to create my factory function createT. Let me start with perfect forwarding.

Perfect Forwarding

First of all: What is perfect forwarding?

  • Perfect forwarding allows you to preserve an argument’s value category (lvalue/rvalue) and const/volatile modifiers.

Perfect forwarding follows a typical pattern, consisting of a universal reference and std::forward.

template<typename T>        // (1)
void create(T&& t){         // (2)
    std::forward<T>(t);     // (3)


The three parts of the pattern to get perfect forwarding are:

  1. You need a template parameter T: typename T
  2. Bind T by universal reference, also known as perfect forwarding reference: T&& t
  3. Invoke std::forward on the argument: std::forward<T>(t)

The key observation is that T&& (line 2) can bind an lvalue or an rvalue and that std::forward (line 3) does the perfect forwarding.

It's time to create the prototype of the createT factory function which should behave at the end such as makeUnique.cpp. I just replaced std::make_unique with the createT call, added the createT factory function, and commented the lines (1) and (4) out. Additionally, I removed the header <memory> (std::make_unique) and added the header <utility>(std::foward).


// createT1.cpp

#include <utility>

struct MyType{
    MyType(int, double, bool){};

template <typename T, typename Arg>
T createT(Arg&& arg){
    return T(std::forward<Arg>(arg));
int main(){
    int lvalue{2020};
    //std::unique_ptr<int> uniqZero = std::make_unique<int>();      // (1)
    auto uniqEleven = createT<int>(2011);                // (2)
    auto uniqTwenty = createT<int>(lvalue);              // (3)
    //auto uniqType = std::make_unique<MyType>(lvalue, 3.14, true); // (4)


Fine. An rvalue (line 2) and an lvalue (line 3) pass my test.

Variadic Templates

Sometimes dots are important. Putting exactly nine dots at the right place and line (1) and line (4) work.


// createT2.cpp

#include <utility>

struct MyType{
    MyType(int, double, bool){};

template <typename T, typename ... Args>
T createT(Args&& ... args){
    return T(std::forward<Args>(args) ... );
int main(){
    int lvalue{2020};
    int uniqZero = createT<int>();                       // (1)
    auto uniqEleven = createT<int>(2011);                // (2)
    auto uniqTwenty = createT<int>(lvalue);              // (3)
    auto uniqType = createT<MyType>(lvalue, 3.14, true); // (4)


How does the magic work? The three dots stand for an ellipse. By using them Args, or args becomes a parameter pack. To be more precise, Args is a template parameter pack and args is a function parameter pack. You can only apply two operations to a parameter pack: you can pack or unpack it. If the ellipse is left of Args the parameter pack is packed; if the ellipse is right of Args the parameter pack is unpacked. In the case of the expression (std::forward<Args>(args)...) this means the expression is unpacked until the parameter pack is consumed and a comma is just placed between the unpacked components. This was all.

CppInsight helps you to look under the curtain.

createT2 1

createT2 2

Now, I'm nearly done. Here is my createT factory function.


template <typename T, typename ... Args>
T createT(Args&& ... args){
    return T(std::forward<Args>(args) ... );


The two missing steps are.

  1. Create a std::unique_ptr<T> instead of a plain T
  2. Rename my function make_unique.

I'm done.



template <typename T, typename ... Args>
std::unique_ptr<T> make_unique(Args&& ... args){
    return std::unique_ptr<T>(new T(std::forward<Args>(args) ... ));


I forgot to scare you. Here is the scary part of my post.


Of course, you know the C function printf. This is its signature: int printf( const char* format, ... );. printf is a function that can get an arbitrary number of arguments. Its power is based on the macro va_arg and is, therefore, not typesafe.

Thanks to variadic templates, printf can be rewritten in a typesafe way.

// myPrintf.cpp

#include <iostream>
void myPrintf(const char* format){                           // (3)
    std::cout << format;
template<typename T, typename ... Args>
void myPrintf(const char* format, T value, Args ... args){   // (4)
    for ( ; *format != '\0'; format++ ) {                    // (5)
        if ( *format == '%' ) {                              // (6) 
           std::cout << value;
           myPrintf(format + 1, args ... );                  // (7)
        std::cout << *format;                                // (8)
int main(){
    myPrintf("\n");                                          // (1)
    myPrintf("% world% %\n", "Hello", '!', 2011);            // (2)


How does the code work? If myPrintf is invoked with only a format string (line 1), line (3) is used. In the case of the line (2), the function template (line 4) is applied. The function templates loops (line 5) as long as the format symbol is not equal to `\0`. If the format symbol is not equal to `\0` , two control flows are possible. First, if the format starts with '%'  (line 6), the first argument value is displayed and myPrintf is once more invoked but this time with a new format symbol and an argument less (line 7). Second, if the format string does not start with '%', the format symbol is just displayed (line 8). The function myPrintf (line 3) is the end condition for the recursive calls.

The output of the program is as expected.


What's next?

One rule to variadic templates is left. Afterwards, the guidelines continue with template metaprogramming. I'm not sure how deep I should dive into template metaprogramming in my next post.






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 CBUIDER STUDIO FINAL ICONS 1024 Small



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

Bookable (Online)


Standard Seminars (English/German)

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


Contact Me

Modernes C++,


Tags: templates

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


Today 6491

Yesterday 7646

Week 39786

Month 106452

All 7374292

Currently are 168 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments