0

I'm comfortable using templates when they simply parameterize over a type. However, I'm now beginning to use them in more complex applications (specifically, parameterizing over a function, as in C++ Template: typename and function to map to int ), and have been reduced essentially to trial and error (note in the above referenced question I can't get my code to compile).

  • What are the exact semantics of the template keyword (or the typename keyword when used outside the definition of a template), especially when you want a function as a parameter?
  • How do I define a template that takes a function as a parameter?
  • Once defined, how do I instantiate and use that template?

I'll accept the first complete answer that includes both a compilable example as well as clear, full explanation, of what's going on behind the magic?


CLARIFICATION: I understand how to use typename. My question is: What are the exact semantics of template definition, especially when applied to invocable parameters? and How do you define and instantiate a class which takes a function (or functor or something invokable) as a template parameter?

Community
  • 1
  • 1
SRobertJames
  • 8,210
  • 14
  • 60
  • 107
  • You can use either [`typename` or `class` keyword](http://stackoverflow.com/questions/213121/use-class-or-typename-for-template-parameters) to alias the type. – Cory Kramer Apr 13 '15 at 12:43
  • Do you want to pass a pointer to a function (which is rarely done, but doable), or a stateless function object (which is what `std::map` does)? – Yakk - Adam Nevraumont Apr 13 '15 at 12:47
  • I feel that templates are lazy abstract syntax tree rewriting. – Basile Starynkevitch Apr 13 '15 at 12:51
  • (There is a 3rd possibility: taking an instance of a type (as a function parameter), where the type is a type parameter of the template, and that instance can be "called" like a function). My problem is that you have many UPDATES, linkes and CLARIFICATIONs, and lots of `...` incomplete code samples. Your code samples do *different* things from my list of 3 options. I could throw you code that does any one of those 3 options, but unless I actually know what you want, that is relatively pointless. Please write, IN THIS QUESTION, a clear problem statement, your best attempt, and what went wrong. – Yakk - Adam Nevraumont Apr 13 '15 at 13:05
  • @djf No, this question is not a duplicate of the linked question. Not even close. Other than both being about templates, they are not remotely related. Unless I am misreading this question completely? If so, can you clarify? – Yakk - Adam Nevraumont Apr 13 '15 at 13:09
  • @Yakk - Thanks, I will work on seeing if I can clarify. You are detecting the root of my very problem: I don't have a clear sense of the semantics of functional parameters to templates, and hence have been reduced to (perhaps inconsistent) trial and error. I will clarify my overall goal: to create and use a class which is templatized over type T, and function f which maps T --> int. Again, not just to copy in some code, but to understand exactly how that works. – SRobertJames Apr 13 '15 at 13:25
  • @SRobertJames do you want `f` to work like it does in a `std::map`? Have you used `std::map` and changed the sorting order? Or do you want to pass in a function pointer? Or do you want it to work like it does in `std::sort` where you pass in a function object, which could be a pointer? These are different techniques. – Yakk - Adam Nevraumont Apr 13 '15 at 13:26
  • @Yakk - That's a great question, and I must say that I don't know enough yet to give a solid preference. I'm amenable to any method, as long as I can truly understand it. – SRobertJames Apr 13 '15 at 13:32

3 Answers3

4

What are the exact semantics of the template and typename keywords, especially when you want a function as a parameter?

template introduces a template declaration (and also declarations of explicit instantiations and specialisations, but that's beyond the scope of this question). It's followed by the list of template parameters, in angle brackets <>.

Within that list, typename or class introduces a type parameter; one of three kinds of parameter, the others being non-type (value) parameters, and template parameters.

How do I define a template that takes a function as a parameter?

The simplest way is with a type parameter, which can be any type that can be called like a function; for example

template <typename F, typename... Args>
void call(F && f, Args &&... args) {f(std::forward<Args>(args)...);}

Once defined, how do I instantiate and use that template?

If it's a function template, simply call the function with appropriately typed arguments. The template parameters will be deduced from them:

void f1(int a, double b);   // function
struct fn {
    void operator()(std::string);
};

call(f1, 42, 1.5);
call(fn{}, "Hello");
call([](std::string s){std::cout << s}, "Lambda\n");

For class templates, you'd have to specify the function type, which can be a bit messy:

template <typename F>
struct thing {
    F f;
};

thing<void(*)(int,double)> thing1 {f1};
thing<fn> thing2 {fn{}};

// Lambdas are problematic since the type has no name
//thing<???> thing3 {[]{std::cout << "Lambda\n";}};

The messiness could be avoided by using a factory function template to deduce the function type and instantiate the class template:

template <typename F>
thing<F> make_thing(F && f) {
    return thing<F>(std::forward<F>(f));
}

auto thing1 = make_thing(f1);
auto thing2 = make_thing(fn{});
auto thing3 = make_thing([]{std::cout << "Lambda\n";});
Mike Seymour
  • 249,747
  • 28
  • 448
  • 644
1

Below some example (commented) code to show how functions can be used in a template. You can replace typename Parameter with typename... Parameter and p to p... if you want the first template to work for any number of fucntion arguments, which uses variadic templates.

#include <functional> // for std::function
#include <iostream>

// example functions
void func(int i)
{
  std::cout << "Integer: " << i << ".\n";
}
int gunc(int i)
{
  int result = i+1;
  std::cout << "+Integer:" << result << ".\n";
  return result;
}

// general non-restrictive template
template<typename Function, typename Parameter>
void call_twice(Function f, Parameter p)
{
  f(p);
  f(p);
}
// Restrict signature to void(int), but the return value can be anything: it will be ignored
void call_thrice(std::function<void(int)> f, int p)
{
  f(p);
  f(p);
  f(p);
}
// Restrict signature to int(int), to exclude func
void call_four_times(std::function<int(int)> f, int p)
{
  f(p);
  f(p);
  f(p);
  f(p);
}

int main()
{
  call_twice(func, 1); // instantiates void call_twice(void (*)(int), int)
  call_twice(gunc, 1); // instantiated void call_twice(int (*)(int), int)
                     // Note I do not write the explicit types of func and gunc in the above comments, they're not important!

  call_thrice(func, 10); // converts func to a std::function<void(int)>
  call_thrice(gunc, 10); // return value can be ignored, so int(int) is convertible to void(int)

  //call_four_times(func, 100); // will fail to compile: cannot convert a function of signature void(int) to one with signature int(int)
  call_four_times(gunc, 100); // converts gunc to std::function<int(int)>
}

Live demo here.


The basic form of a template declaration starts anything of the form

template<typename T>
template<class T> // same as above
template<int N> // explicitly typed template parameter, need not be an int
template<typename... variadic_template_args>

And of course the forms with combinations of the above, including nested template types and all. Just remember the variadic template parameter must come last.

Templates are what the name implies: a blueprint to create a class with certain functionality expressed within the template definition. Instantiation occurs when you actually call a template function (from either another instantiated template function or a non-template function), or for class templates, when you declare an object of the type, e.g. for std::vector:

std::vector<double> v; // instantiate the class template std::vector for type double

This is no different to templates that take functions as arguments. Note that functions decay to function pointers when passed to another function.


For example, a class template with a function as template parameter:

#include <iostream>
#include <type_traits> // for std::result_of

template<typename Function, Function f>
struct A
{
  using function_type = Function;
  template<typename... ArgTypes>
  typename std::result_of<Function(ArgTypes...)>::type call(ArgTypes... args)
  {
    return f(args...);
  }
};

void func(int i) { std::cout << "Integer: " << i << ".\n"; }

int main()
{
  A<decltype(&func), func> a;
  a.call(42);
}

Live demo here. Note the extra & in the decltype, because a function only decays to a function pointer when it is passed to a function, not when it is used as a template parameter. Note the "extra" typename before the std::result_of: this is required because its type typedef is a dependent name. These and other times you need to use the template and typename keywords, those are covered in Where and why do I have to put the “template” and “typename” keywords?

Community
  • 1
  • 1
rubenvb
  • 74,642
  • 33
  • 187
  • 332
  • If, instead of templating a function, you were templating a class, and wanted to pass a function as a parameter, how do you instantiate that class? Whenever I've instantiated a template, I pass it a type - how do I pass it a specific function or functor object at instantiatian time? – SRobertJames Apr 13 '15 at 13:29
  • @SRobertJames see my second example code in my updated answer. – rubenvb Apr 13 '15 at 14:04
0

It's easier to use lambdas

template <typename F>
void herp(F function)
{
    function("Hello from herp");
}


int main()
{
    const auto deDerp = [] (const std::string& msg)
    {
        std::cout << msg << "\n";
    };

    herp(deDerp);
}
James
  • 9,064
  • 3
  • 31
  • 49
  • Thanks, but I unfortunately don't have C+11 lambda support, and, besides, would like to _understand_ how the exact semantics regardless – SRobertJames Apr 13 '15 at 12:50