Various Template Improvements with C++20


Admittedly, I present in this post a few minor improvements to templates and C++20 in general. Although these improvements may not seem so impressive to you, they make C++20 more consistent and less error-prone when you program generic.


Today's post is about conditionally explicit constructors and new non-type template parameters.

Conditionally Explicit Constructors

Sometimes, you want to have a class that should has constructors accepting various types. For example, you have a class VariantWrapper which holds a std::variant accepting different types.

class VariantWrapper {

    std::variant<bool, char, int, double, float, std::string> myVariant;



To initialize the myVariant with bool, char, int, double, float, or std::string, the class VariantWrapper needs constructors for each listed type. Laziness is a virtue - at least for programmers - , therefore, you decide to make the constructor generic. 

The class Implicit exemplifies a generic constructor.

// explicitBool.cpp

#include <iostream>
#include <string>
#include <type_traits>

struct Implicit {
    template <typename T>        // (1)
    Implicit(T t) {
        std::cout << t << std::endl;

struct Explicit {
    template <typename T>
    explicit Explicit(T t) {    // (2)
        std::cout << t << std::endl;

int main() {
    std::cout << std::endl;
    Implicit imp1 = "implicit";
    Implicit imp2("explicit");
    Implicit imp3 = 1998;
    Implicit imp4(1998);
    std::cout << std::endl;
    // Explicit exp1 = "implicit";  // (3)
    Explicit exp2{"explicit"};      // (4)
    // Explicit exp3 = 2011;        // (3)
    Explicit exp4{2011};            // (4)
    std::cout << std::endl;  



Now, you have an issue. A generic constructor (1) is a catch-all constructor because you can invoke them with any type. The constructor is way too greedy.  By putting an explicit in front of the constructor (2). the constructor becomes explicit. This means that implicit conversions (3) are not valid anymore. Only explicit calls (4) are valid.

Thanks to Clang 10, here is the output of the program:


This is not the and of the story. Maybe, you have a type MyBool that should only support the implicit conversion from bool, but no other implicit conversion. In this case, explicit can be used conditionally.

// myBool.cpp

#include <iostream>
#include <type_traits>
#include <typeinfo>

struct MyBool {
    template <typename T>
    explicit(!std::is_same<T, bool>::value) MyBool(T t) {  // (1)
        std::cout << typeid(t).name() << std::endl;

void needBool(MyBool b){ }                                 // (2)

int main() {

    MyBool myBool1(true);                              
    MyBool myBool2 = false;                                // (3)
    needBool(true);                                        // (4)
    // needBool(5);
    // needBool("true");


The explicit(!std::is_same<T,  bool>::value) expression guarantees that MyBool can only be implicitly created from a bool value. The function std::is_same is a compile-time predicate from the type_traits library. Compile-time predicate means std::is_same is evaluated at compile-time and returns a boolean. Consequently, the implicit conversion from bool in (3) and (4) is possible, but not the commented-out conversions from int and a C-string.

You are right when you argue that a conditionally explicit constructor would be possible with SFINAE. But honestly, I wouldn't say I like the corresponding SFINAE using a constructor, because it would take me a few lines to explain it. Additionally, I only get it right after the third try.

template <typename T, std::enable_if_t<std::is_same_v<std::decay_t<T>, bool>, bool> = true>
MyBool(T&& t) {
    std::cout << typeid(t).name() << std::endl;


I should add a few explaining words. std::enable_if is a convenient way to use SFINAE. SFINAE stands for Substitution Failure Is Not An Error and applies during overload resolution of a function template. When substituting the template parameter fails, the specialization is discarded from the overload set but causes no compiler error. This exactly happens in this concrete case. The specialization is discarded if std::is_same_v<std::decay_t<T>, bool> evaluates to false. std::decay<T> applies conversions to T, such as removing const, volatile or a reference from T. std::decay_t<T> is a convenient syntax for std::decay<T>::type. The same holds for std::is_same_v<T, bool>, which is short for std::is_same<T, bool>::value.

As my German reader pre alpha pointed out: the constructor using SFINAE is way too greedy. It disables all non-bool constructors.

Beside my longish explanation, an additional argument speaks against SFINAE and for a conditionally explicit constructor: performance. Simon Brand pointed out in his post "C++20's Conditionally Explicit Constructors", that explicit(bool) made the template instantiation for Visual Studio 2019 about 15% faster compared to SFINAE.

With C++20, additional non-type template parameters are supported.


Rainer D 6 P2 540x540Modernes C++ Mentoring

Be part of my mentoring programs:





Do you want to stay informed about my mentoring programs: Subscribe via E-Mail.

New non-type Template Parameter

With C++20, floating points and classes with constexpr constructors are supported as non-types.

C++ supports non-types as template parameters. Essentially non-types could be

  • integers and enumerators
  • pointer  or references to objects, functions, and to attributes of a class
  • std::nullptr_t

When I ask the students in my class if they ever used a non-type as a template parameter, they say: No! Of course, I answer my tricky question and show an often-used example for non-type template parameters:

std::array<int, 5> myVec;


5 is a non-type and is used as a template argument. We are just used to it. Since the first C++-standard C++98, there is a discussion in the C++ community to support floating points as a template parameter. Now, we C++20, we have it:


// nonTypeTemplateParameter.cpp

struct ClassType {
    constexpr ClassType(int) {}  // (1)

template <ClassType cl>          // (2)
auto getClassType() {
    return cl;

template <double d>              // (3)
auto getDouble() {
    return d;

int main() {

    auto c1 = getClassType<ClassType(2020)>();

    auto d1 = getDouble<5.5>();  // (4)
    auto d2 = getDouble<6.5>();  // (4)



ClassType has a constexpr constructor (1) and can be used as a template argument (2). The same holds for the function template getDouble (3), which accepts only doubles. I want to emphasize that each call of the function template getDouble (4) with a new argument triggers the instantiation of a new function getDouble.  This means that two instantiations for the doubles 5.5 and 6.5 are created.

If Clang would already supports this feature, I could show you with C++ Insights that each instantiation for 5.5 and 6.5 creates a fully specialized function template. At least, thanks to GCC, I can show you the relevant assembler instructions with the Compiler Explorer.


The screenshot shows that the compiler created for each template argument a function.

What's next?

As templates, lambdas are also improved in various ways in C++20. My next post is about these various improvements.



Thanks a lot to my Patreon Supporters: Matt Braun, Roman Postanciuc, Tobias Zindl, G Prvulovic, Reinhold Dröge, Abernitzke, Frank Grimm, Sakib, Broeserl, António Pina, Sergey Agafyin, Андрей Бурмистров, Jake, GS, Lawton Shoemake, Animus24, Jozo Leko, John Breland, Venkat Nandam, Jose Francisco, Douglas Tinkham, Kuchlong Kuchlong, Robert Blanch, Truels Wissneth, Kris Kafka, Mario Luoni, Friedrich Huber, lennonli, Pramod Tikare Muralidhara, Peter Ware, Daniel Hufschläger, Alessandro Pezzato, Bob Perry, Satish Vangipuram, Andi Ireland, Richard Ohnemus, Michael Dunsky, Leo Goodstadt, John Wiederhirn, Yacob Cohen-Arazi, Florian Tischler, Robin Furness, Michael Young, Holger Detering, Bernd Mühlhaus, Matthieu Bolt, Stephen Kelley, Kyle Dean, Tusar Palauri, Dmitry Farberov, Juan Dent, George Liao, Daniel Ceperley, Jon T Hess, Stephen Totten, Wolfgang Fütterer, Matthias Grün, Phillip Diekmann, Ben Atakora, Ann Shatoff, and Rob North.


Thanks, in particular, to Jon Hess, Lakshman, Christian Wittenhorst, Sherhy Pyton, Dendi Suhubdy, Sudhakar Belagurusamy, Richard Sargeant, Rusty Fleming, John Nebel, Mipko, Alicja Kaminska, and Slavko Radman.



My special thanks to Embarcadero CBUIDER STUDIO FINAL ICONS 1024 Small


My special thanks to PVS-Studio PVC Logo


My special thanks to logo


My special thanks to Take Up code TakeUpCode 450 60



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.

  • C++ - The Core Language
  • C++ - The Standard Library
  • C++ - Compact
  • C++11 and C++14
  • Concurrency with Modern C++
  • Design Pattern and Architectural Pattern with C++
  • Embedded Programming with Modern C++
  • Generic Programming (Templates) with C++


  • Clean Code with Modern C++
  • C++20

Contact Me

Modernes C++,



0 #1 FederAndInk 2020-07-29 20:30
What's cool also is that you can do explicit(false) to say explicitly that it is an implicit ctor or cast

Stay Informed about my Mentoring



English 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

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

Course: Master Software Design Patterns and Architecture in C++

Subscribe to the newsletter (+ pdf bundle)

All tags

Blog archive

Source Code


Today 2260

Yesterday 4371

Week 38067

Month 168192

All 12055958

Currently are 146 guests and no members online

Kubik-Rubik Joomla! Extensions

Latest comments