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.
Just curious, is there any benefit to using both `constexpr` and `const` when declaring a variable? I assumed that `constexpr` already implies `const`.
It is indeed redundant. constexpr implies const.