Henrik Bøgelund
Henrik Bøgelund

Reputation: 81

How can I use template expansion on a std::function

I am trying to use the std::function::target() in a template. First I tried the example found here: http://www.cplusplus.com/reference/functional/function/target/ It worked well, so I modified it to allow a function to be passed as parameter. This also worked - see test1() in the code listing below.

Then I tried making it more generic, by using a template. See test2() below. But I clearly have not gotten the template correct, and I get the following compiler output:

targetTest.cpp: In function ‘void test2(std::function<int(Args ...)>)’:
targetTest.cpp:36:29: error: expected primary-expression before ‘int’
   std::cout << (*foo.target<int(*)(Args...)>())(100,20) << '\n';
                             ^~~
targetTest.cpp:36:29: error: expected ‘)’ before ‘int’
targetTest.cpp:40:15: error: expected primary-expression before ‘int’
   *foo.target<int(*)(Args...)>() = &my_minus;
               ^~~
targetTest.cpp:40:15: error: expected ‘;’ before ‘int’
targetTest.cpp: In function ‘void myTestFunction()’:
targetTest.cpp:47:26: error: no matching function for call to ‘test2<int, int>(int (&)(int, int))’
   test2<int, int>(my_plus);
                          ^
targetTest.cpp:27:6: note: candidate: template<class ... Args> void test2(std::function<int(Args ...)>)
 void test2(std::function<int(Args...)> foo)
      ^~~~~
targetTest.cpp:27:6: note:   template argument deduction/substitution failed:
targetTest.cpp:47:26: note:   mismatched types ‘std::function<int(Args ...)>’ and ‘int (*)(int, int)’
   test2<int, int>(my_plus);

Here is my testcode:

// function::target example
#include <iostream>     // std::cout, std::boolalpha
#include <functional>   // std::function, std::plus, std::minus

int my_plus (int a, int b) {return a+b;}
int my_minus (int a, int b) {return a-b;}

void test1(std::function<int(int,int)> param)
{
  std::function<int(int,int)> foo = param;
  std::function<int(int,int)> bar = std::plus<int>();

  // calling using functional form:
  std::cout << foo(100,20) << '\n';
  std::cout << bar(100,20) << '\n';

  // calling by invoking target:
  std::cout << (*foo.target<int(*)(int,int)>())(100,20) << '\n';
  std::cout << (*bar.target<std::plus<int>>())(100,20) << '\n';

  // changing target directly:
  *foo.target<int(*)(int,int)>() = &my_minus;
  std::cout << foo(100,20) << '\n';
}

template<typename... Args>
void test2(std::function<int(Args...)> foo)
{
  std::function<int(int,int)> bar = std::plus<int>();

  // calling using functional form:
  std::cout << foo(100,20) << '\n';
  std::cout << bar(100,20) << '\n';

  // calling by invoking target:
  std::cout << (*foo.target<int(*)(Args...)>())(100,20) << '\n';
  std::cout << (*bar.target<std::plus<int>>())(100,20) << '\n';

  // changing target directly:
  *foo.target<int(*)(Args...)>() = &my_minus;
  std::cout << foo(100,20) << '\n';
}

void myTestFunction()
{
  test1(my_plus);
  test2<int, int>(my_plus);
}

Upvotes: 1

Views: 55

Answers (1)

SergeyA
SergeyA

Reputation: 62553

The reason for immediate compilation error you are seeing is an annoying requirement to use template keyword you need to use whenever you are trying to use something as a template, but compiler doesn't know if it is indeed a template. And the reason why compiler doesn't know that target is a template is because of two-step template instantiation (this topic is greatly researched here on SO and everywhere, so I will not discuss it here).

Here is a fixed line from your sample:

std::cout << (*foo.template target<int(*)(Args...)>())(100,20) << '\n';

This works in this particular place.

However, your code still would not compile, due to the way you are trying to call the templated test2. Correct way would be:

test2(std::function<int (int, int)>(my_plus));

Which allows compiler to deduce template arguments.

Upvotes: 3

Related Questions