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.