4

Consider a set of functions like

template< class Fun >
void A( const Fun& )
{
}

template< class Fun >
void B( const Fun& )
{
}

template< class Fun >
void C( const Fun& )
{
}

designed to take function types as arguments. Then, this is perfectly ok:

template< class T >
void Func( const T& )
{
}

A( Func< int > );
B( Func< int > );
C( Func< int > );

Now I wanted to get rid of repeating the int temaplate argument so I tried this:

template< class T >
struct Helper
{
  template< template< class > class Fun >
  static void A( Fun< T >& f )
  {
    A( f );
  }

  template< template< class > class Fun >
  static void B( Fun< T >& f )
  {
    B( f );
  }

  ...
};

typedef Helper< int > IntHelper;
IntHelper::A( Func );  //error
IntHelper::B( Func );  //
IntHelper::C( Func );  //

however this fails to compile on gcc 4.5.1 ('error: no matching function for call to 'Helper<int>::A(<unresolved overloaded function type>)') and MSVC10 (cannot use function template 'void Func(const T &)' as a function argument and could not deduce template argument for 'overloaded function type' from 'overloaded function type').

Can someone explain why exactly, and is there a way around this?

edit ok I understand why it is not possible now; for the answers containing a workaround: in the actual code there are a lot of different Funcs, say 100, while there's only about 6 function like A, B and C...

stijn
  • 34,664
  • 13
  • 111
  • 163

4 Answers4

3

Func is a function template, so you can't pass it as a value to a function.

You also can't pass it as a template template parameter, because template template parameters have to be class templates (not function templates).

You could pass a template template parameter that wraps the function template (e.g. returning its instantiation from a static member function):

template<class T> struct FuncHelper {
    static void (*f())(const T &) { return &(Func<T>); }
};
template<typename T>
struct Helper
{
  template< template< class > class Fun >
  static void A()
  {
    A( Fun<T>::f() );
  }
};
Helper<int>::A<FuncHelper>();
Community
  • 1
  • 1
ecatmur
  • 152,476
  • 27
  • 293
  • 366
3

The form template<class> class Fun, whether as a declaration or as a template template parameter (like you have), is designed for class templates only, which Func isn't. It's a function template. Those have the form template</*parameters*/> Ret foo(/*parameters*/), and they aren't allowed as template template parameters.

Generally speaking function templates cannot be manipulated as much as class templates.

There is one situation where you can be spared the need to pass template parameters around:

// Deduces that Func<int> is meant
void (*p)(int) = Func;

Then you can pass p to A, B and C.

(Similarly if you have a function void f(void(*p)(int)); then a call of the form f(Func) is fine.)

Luc Danton
  • 34,649
  • 6
  • 70
  • 114
3

While is is possible to use a class template as template parameter, e.g.

template <typename> class Foo;

template <template <typename> class C> void doit() { /* ...*/ };

doit<Foo>();

it is (semantically) not possible to use a function template as template parameter (there is no "function template pointer"). The usual way around is using a function object, e.g.

template <typename T>
struct Func
{
  void operator()(T const &) const
  {
     /* ... */
  }
};


template <typename T>
struct helper
{
  template <template <typename> class F>
  static void A()
  {
    A(F<T>);
  }
  // etc
};

typedef helper<int> int_helper;

int_helper::A<Func>();
MadScientist
  • 3,390
  • 15
  • 19
1

If Func may be declared as a lambda with an auto type parameter (using C++14's generic lambdas), then the definitions of A, B, and C do not need to change, and can be called without having to specify the parameter type:

auto Func = [](auto const&)
{
};

A(Func);
B(Func);
C(Func);

Live Demo

monkey0506
  • 2,489
  • 1
  • 21
  • 27