• Home
  • /
  • Blog
  • /
  • Various Template Improvements with C++20

July 24, 2020

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.

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.



Leave a Reply

Your email address will not be published. Required fields are marked

{"email":"Email address invalid","url":"Website address invalid","required":"Required field missing"}

Related Posts