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>; 1 2 3 4 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>; 1 2 3 4 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 1 2 3 4 5 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>; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 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>; 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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>; 1 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>; 1 2 3 4 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.

Share this: Facebook

Twitter

Print

More

Email

Reddit



