Starting C++17, you can use auto and decltype(auto) to declare non-type template parameters. This new
feature provides a way to write generic code for non-type parameters of different types. Also, it allows, by using variadic templates, to make a
template take a list of non-type template parameters of different types: template<auto... VS> class A.
If the type is used in the template definition, you can replace it with auto, or decltype if you want to underline that
the type is the same as of the template parameter. Note, that you can use template <class T> T packed_t(T...); to get the type of
arguments in the auto... pack (see the "Compliant Solution" section below).
This rule detects the common pattern where a type template parameter is introduced only to be used as a type for the next non-type template
parameter(s).
Noncompliant code example
template <typename T, T value>
struct A { // Noncompliant
inline static auto field = value;
typedef T type;
static T anotherField;
};
template <typename T, T... values>
struct MultiA { // Noncompliant
inline static std::vector vec = { values... };
};
template <typename T, T defaultVal>
T foo(T arg) {
return arg > 0 ? arg : defaultVal;
}
void f() {
A<int, 1> a1;
A<bool, false> a2;
MultiA<int, 1, 2, 3, 4> multiA1;
MultiA<char, 'a', 'b'> multiA2;
foo<int, 1>(-1);
}
Compliant solution
template <auto value>
struct A { // Compliant
inline static auto field = value;
typedef decltype(value) type;
static type anotherField;
};
template <auto ... values>
struct MultiA { // Compliant
inline static std::vector vec = { values... };
};
template <auto defaultVal>
auto foo(decltype(defaultVal) arg) {
return arg > 0 ? arg : defaultVal;
}
void f() {
A<1> a1;
A<false> a2;
MultiA<1, 2, 3, 4> multiA1;
MultiA<'a', 'b'> multiA2;
foo<1>(-1);
}
// Get the type out of auto... declaration
template <class T>
T packed_t(T...);
template <auto... Is>
std::vector<std::string> name_copy(std::map<decltype(packed_t(Is...)), std::string> names) {
return {names[Is]...};
}