问题
I have the following class template:
template<class T, unsigned N>
class MyClass;
where T is some type, N - number of components. It is possible to initialize the class using MyClass{a1, a2, a3} where the number of arguments is equal to N.
I want to add a member function template (let's name it foo) of MyClass that would meet the following requirements:
- It is templated by another type
T2(i.e.template<class T2> void foo(..)) - It accepts enough data to construct
MyClass<T,N>, but not less and not more. Violating this results in a compile-time error. - It deduces
T2from the types of the parameters. I.e. I want that it would be possible to callfoo({a1, a2, a3})orfoo(a1, a2, a3)or similar, without typing<double>orMyClass<double,N>every time.
Is there a way to implement the function so that the above requirements are satisfied?
I've already thought about and/or tried the following solutions:
1) The obvious one:
...
template<class T2>
void foo(MyClass<T2, N> arg);
...
a.foo({1,2,3}); //compile-time error
Can't work in principle, because braced initializer lists are a non-deduced context, thus they can't deduce any types. That's quite unfortunate, I'd be very happy if this worked.
2) initializer_list
Can't work in principle, because it can't check the number of arguments at compile-time.
3) Variadic template magic
Something like the function below would be neat:
template<class...T2, class std::enable_if<sizeof...(T2) == N, int>::type = 0>
void foo(T2... args);
..
foo(1,2,3);
However, I couldn't get it to work - T2 still couldn't be deduced. Maybe someone knows why? I used GCC4.7 20120121 snapshot.
4) The ugly one
Essentially this is the same as the above one, just expanded into several overloads for different N. I would better reimplement MyClass as a set of specializations for different Ns than to use this one.
template<class T2, class std::enable_if<N == 1, int>::type = 0>
void fun(T2 a1); //if N == 1
template<class T2, ..>
void fun(T2 a1, T2 a2); //if N == 2
template<class T2, ..>
void fun(T2 a1, T2 a2, T2 a3); //if N == 3
...
回答1:
Your third variant's second non-type param should have prefix typename not class :
template<class...T2, typename std::enable_if<sizeof...(T2) == N, int>::type = 0>
void foo(T2... args);
..
foo(1,2,3);
Check it
Gcc 4.7.0 snapshots has some bugs with templates I guess, if you try it with gcc 4.6.2/1 it shall work.
回答2:
Why not use a static_assert?
template <typename T, size_t N>
class MyClass
{
public:
template <typename... Args>
void foo(Args&&... args)
{
static_assert(sizeof...(Args) == N, "Wrong number of arguments.");
// Rest of the implementation.
}
};
回答3:
Maybe like this:
#include <utility>
#include <type_traits>
template <typename T, unsigned int N>
class MyClass
{
template <typename ...Args>
typename std::enable_if<std::is_constructible<MyClass<T, N>, Args...>::value>::type
foo(Args &&... args)
{
// for example:
MyClass<T, N> m(std::forward<Args>(args)...);
// ...
}
};
This will work only if MyClass has a constructor that accepts the relevant arguments directly (like MyClass(A1, A2, A3)), but I don't think it works if MyClass has a constructor that requires an initializer list, nor will it work if MyClass is an aggregate that requires brace-initialization.
That said, it doesn't look like your MyClass could possibly accept an initializer list, since you said that it has to take precisely N arguments, which an IL cannot promise.
回答4:
May be, based on your 3rd variant, you can extract first argument from initializer list and deduce type by it
template<class U, class...T2, class std::enable_if<sizeof...(T2) == N-1, int>::type = 0>
void foo(U u, T2... args)
{
MyClass<U, N>({u, args...});
}
来源:https://stackoverflow.com/questions/9100680/member-function-template-with-the-number-of-parameters-depending-on-an-integral