template <auto>

If you wanted to create templates with non-type template parameters, you had to specify both the type and the value. In C++17, this is no longer the case, as template <auto> helps simplify these scenarios.

Let’s take as an example the declaration of a constant template.

template <typename T, T value> 
constexpr T numeric_constant = value;

constexpr auto const the_answer = numeric_constant<int, 42>;

In C++17, this can be simplified as follows:

template <auto value> 
constexpr auto numeric_constant = value;

constexpr auto const the_answer = numeric_constant<42>;

You no longer need to specify the type for non-type template parameters, it is automatically deduced by the compiler. Here is another example:

template <auto value> 
void foo() { /*... */ }

foo<42>();      // deduces int
foo<false>();   // deduces bool

Let’s look at an example where template <auto> can simplify the code. The standard defines a type called std::integral_constant that wraps a static constant of a specified type and is the base class for the C++ type traits. std::true_type and std::false_type are two of those. These could be implemented as follows:

template<class T, T val>
struct integral_constant
{
   static constexpr T value = val;

   using value_type = T;
   using type = integral_constant;

   constexpr operator value_type() const noexcept { return value; }

   [[nodiscard]] constexpr value_type operator()() const noexcept { return value; }
};

template<bool val>
using bool_constant = integral_constant<bool, val>;

using true_type = bool_constant<true>;
using false_type = bool_constant<false>;

With template <auto> this code can be written as following:

template<auto val>
struct integral_constant
{
   static constexpr auto value = val;

   using value_type = decltype(val);
   using type = integral_constant;

   constexpr operator value_type() const noexcept { return value; }

   [[nodiscard]] constexpr value_type operator()() const noexcept { return value; }
};

using true_type = integral_constant<true>;
using false_type = integral_constant<false>;

Note: Although this works fine with Clang and GCC, VC++ 15.7 complains about the use of decltype(val).

Note: It may be more cumbersome to deduce some types. For instance, if you need a short type, there is no way to specify a short literal. You must indicate that with a cast. In order words, if you want a short 42 integral constant, you need to declare it like this:

using short_42 = integral_constant<(short)42>;

This feature is also useful with variadic templates for creating compile-time lists of henerogenous values. Here is an example:

template <auto ... vs>
struct heterogenous_list {};

using ultimate_list = heterogenous_list<42, "the ultimate answer", true>;

For more information on this topic, see Declaring non-type template arguments with auto.

2 Replies to “template <auto>”

  1. Just curious, is there any benefit to using both `constexpr` and `const` when declaring a variable? I assumed that `constexpr` already implies `const`.

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.