I saw a blog post which used non-type variadic templates (currently not supported by gcc, only by clang).
template
stru
Luc Danton's solution doesn't behave correctly with parameters which are not of type int
, but can be implicitly converted to an int
. Here's one which does:
template<typename T, typename U> struct first_type { typedef T type; };
template<typename T> int max_checked(T n) { return n; }
template<typename T1, typename T2, typename ...Ts>
int max_checked(T1 n1, T2 n2, Ts ...ns)
{
int maxRest = max_checked(n2, ns...);
return n1 > maxRest ? n1 : maxRest;
}
template<typename ...T> auto max(T &&...t) ->
decltype(max_checked<typename first_type<int, T>::type...>(t...))
{
return max_checked<typename first_type<int, T>::type...>(t...);
}
struct S { operator int() { return 3; } };
int v = max(1, 2.0, S()); // v = 3.
Here, max
forwards all arguments unchanged to max_checked
, which takes the same number of arguments of type int
(provided by performing a pack-expansion on the first_type
template). The decltype(...)
return type is used to apply SFINAE if any argument can't be converted to int
.
Here are two ways of defining a variadic function template only accepting int
parameters. The first one generates a hard-error when instantiated, the second uses SFINAE:
template<typename... T>
struct and_: std::true_type {};
template<typename First, typename... Rest>
struct and_
: std::integral_constant<
bool
, First::value && and_<Rest...>::value
> {};
template<typename... T>
void
foo(T... t)
{
static_assert(
and_<std::is_same<T, int>...>::value
, "Invalid parameter was passed" );
// ...
}
template<
typename... T
, typename = typename std::enable_if<
and_<std::is_same<T, int>...>::value
>::type
>
void
foo(T... t)
{
// ...
}
As you can see, non-type template parameters aren't used here.
This will print out all elements, get max could be implemented similarly
template <int N>
void foo(){
cout << N << endl;
}
template <int N, int M, int ... Rest>
void foo(){
cout << N << endl;
foo<M, Rest...>();
}
int main(){
foo<1, 5, 7>();
return 0;
}
You are simply confusing type names and non-type names. What you want simply doesn’t work.
You can probably use variadic non-type templates in functions, but not as (non-template) arguments:
template <int N, int... Rest>
int max()
{
int tmp = max<Rest...>();
return N < tmp ? tmp : N;
}
std::cout << max<3, 1, 4, 2, 5, 0>() << std::endl;
… although I haven’t tested this and I’m not sure how this should work given that you need to have a partial specialisation as the base case. You could solve this by dispatching to a partially specialised struct:
template <int N, int... Rest>
struct max_t {
static int const value = max_t<Rest...>::value > N ? max_t<Rest...>::value : N;
};
template <int N>
struct max_t<N> {
static int const value = N;
};
template <int... NS>
int max()
{
return max_t<NS...>::value;
}
This will work.
Here's how you could achieve variadic args in your max
example such that it can take any number of arithmetic arguments:
template<typename T, typename ... Ts>
struct are_arithmetic{
enum {
value = std::is_arithmetic<T>::value && are_arithmetic<Ts...>::value
};
};
template<typename T>
struct are_arithmetic<T>{
enum {
value = std::is_arithmetic<T>::value
};
};
template<typename Arg, typename = typename std::enable_if<std::is_arithmetic<Arg>::value>::type>
Arg max(Arg arg){
return arg;
}
template<typename Arg, typename Arg1, typename ... Args, typename = typename std::enable_if<are_arithmetic<Arg, Arg1, Args...>::value>::type>
auto max(Arg arg, Arg1 arg1, Args ... args){
auto max_rest = max(arg1, args...);
return arg > max_rest ? arg : max_rest;
}
int main(){
auto res = max(1.0, 2, 3.0f, 5, 7l);
}
This is good because it can take any numeric type and will return the maximum number by its original type rather than just int
, too.