Member Function of the Variadic Template Class

I am having a problem when I try to create a member function variable with a paramater package of a certain type.

template <typename T> struct A { using result_type = T; T operator()(T a, T b) { return a+b; } }; template <typename Functor> struct B { using T = typename Functor::result_type; T operator()(Functor &&f, T... args) { return f(args...); } }; 

It is expected to work as follows:

 A<int> a; B<A<int>> b; int result = b(a, 2, 3); // should return 5 

However, I get the following errors:

 error: type 'T' (aka 'typename Functor::result_type') of function parameter pack does not contain any unexpanded parameter packs T operator()(Functor &&f, T... args) ~^~~~~~~~ error: pack expansion does not contain any unexpanded parameter packs return f(args...); ~~~~^ 

What would be the right way to achieve the expected functionality?

+7
c ++ c ++ 11 templates
source share
4 answers

The parameter package can only be used if the function is a function template.

From http://en.cppreference.com/w/cpp/language/parameter_pack :

A template parameter package is a template parameter that takes zero or more template arguments (not types, types, or templates). A function parameter package is a function parameter that takes zero or more function arguments.

A template with at least one package of parameters is called a variation template.

  template <typename ... Args> T operator()(Functor&& f, Args... args) { return f(args...); } 

In addition, using && in the function above makes sense only if it is a template parameter. When you use && for an argument without the type being a template parameter, you cannot use:

  A<int> a; B<A<int>> b; int r = b(a, 2, 3); 

However you can use

 int r = b(std::move(a), 2, 3); 

Make your choice. Save the argument type as is and use std::move(a) or change the function to use a simple link

  template <typename ... Args> T operator()(Functor& f, Args... args) { return f(args...); } 

and use

  int r = b(a, 2, 3); 

Update

You can use a helper class to make sure that all arguments are of the correct type.

 template<typename ... Args> struct IsSame : public std::false_type {}; template<typename T> struct IsSame<T> : public std::true_type {}; template<typename T, typename ... Args> struct IsSame<T, T, Args...> : public std::true_type { static const bool value = IsSame<T, Args ...>::value; }; 

and use:

 template <typename ... Args> T operator()(Functor&& f, Args... args) { static_assert(IsSame<T, Args...>::value, "Invalid argument type"); return f(args...); } 

Wherein

 A<int> a; B<A<int>> b; int r = b(std::move(a), 2, 3); 

still working but

 r = b(std::move(a), 2, 3.0); 

not executed.

I do not know if you need to be strict with argument types. You have a way if you need to.

+4
source share

One idea is to use std::initializer_list instead, which will force the same type (of course, you can probably get around this with a variational pattern and some clever use of std::is_same to force the use of the same same type for all parameters variational pattern):

 #include <algorithm> #include <initializer_list> #include <utility> #include <iostream> template <typename T> struct A { using result_type = T; T operator()(std::initializer_list<result_type> const& li) { return std::accumulate(std::begin(li), std::end(li), 0.); } }; template <typename Functor> struct B { using T = typename Functor::result_type; T operator()(Functor &&f, std::initializer_list<T> args) { return f(args); } }; int main() { A<int> functor; B<decltype(functor)> test; std::cout << test(std::move(functor), {1, 2, 3}); // displays 6 } 

Live on coliru

+2
source share

Some SFINAE tricks can be used, for example:

 struct Foo {}; template<class T, class...> struct all_same : std::true_type {}; template<class T, class U, class... SS> struct all_same<T, U, SS...> : std::integral_constant<bool, std::is_same<T,U>{} && all_same<T, SS...>{}> {}; 

Then

 template <typename Functor> struct B { using T = typename Functor::result_type; template<typename ...Args> T operator()(Functor&& f, Args... args) { static_assert(all_same<T, Args...>{}, "all not same types"); return f(args...); } }; 

Here demo

+2
source share

You must use the argument package. Also, why are you trying to pass the rvalue link?

 template <typename Functor> struct B { using T = typename Functor::result_type; template<typename ...Args> T operator()(Functor f, Args... args) { return f(args...); } }; 

Edit: if you want to verify that all arguments are of type T, you can declare a validation structure:

 template <typename T, typename ...Pack> struct verify_params {}; template <typename T> struct verify_params<T> { using val=void; }; template <typename T, typename ...Pack> struct verify_params<T,T,Pack...> { using val=typename verify_params<T,Pack...>::val; }; 

And then you can add a string like (typename verify_params<T,Args...>::val)0; .

+1
source share

All Articles