1

I am trying to use a template that takes in an std::function, but the template argument deduction is failing.

double foo(){
  return 2.3;
}

template <typename V>
void funcC (V (*fptr)()){
  std::cout << "C function's value is\"" << fptr() << '\"' << std::endl;
}

template <typename V>
void funcCxx (std::function<V()> fptr){
  std::cout << "C++ function's value is\"" << fptr() << '\"' << std::endl;
}

funcC (foo);
funcCxx (foo);

The C function-pointer style (funcC) works, but the C++ std::function (funcCxx) doesn't.

I get this compiler error candidate template ignored: could not match 'function<type-parameter-0-0 ()>' against 'double (*)()'

Any idea what causes this error? Just in case, this is being compiled in clang with C++17, but I don't think it's a compiler error.

Nathan29006781
  • 173
  • 1
  • 9
  • What _you seem to know_ is that if `std::function` were instantiated with every imaginable template argument, there would be **exactly one** instantiation - `std::function` that would happen to have an implicit constructor that would accept `foo` as a parameter. Your C++ compiler does not know this and will not check. It would be too costly. – Drew Dormann Jan 24 '22 at 19:29
  • What is your goal? Are you trying to deduce the return type of the functor argument? Or are you trying to restrict the function template to only arguments that are invocable with no parameters? – parktomatomi Jan 24 '22 at 19:47
  • @parktomatomi Both. The function should work for int(), double(), string() and deduce the type. But int(char), double(int, char), string(string) should not work at all. – Nathan29006781 Jan 24 '22 at 19:50

2 Answers2

2

It cannot be deduced because you are not passing a std::function to funcCxx.

Function argument and parameter type must match in template argument deduction. Otherwise deduction fails.

You could let the function take any type instead of constraining it to function pointers or std::function and then you can construct the std::function inside the function:

template <typename V>
void funcCxx (V&& v){
    auto f = std::function(std::forward<V>(v));
    std::cout << "C++ function's value is\"" << f() << '\"' << std::endl;
}
user17732522
  • 53,019
  • 2
  • 56
  • 105
  • Thank you, I know this is an unrelated question, but is it possible to do this for non-static member functions? Example: `Class cl{ bool tr(){return true;} }; cl bar; funcCxx(bar.tr);` – Nathan29006781 Jan 24 '22 at 19:46
  • @Nathan29006781 https://stackoverflow.com/questions/7582546/using-generic-stdfunction-objects-with-member-functions-in-one-class. Note that the `std::bind` version is out-dated, use the lambda method described in the answers. Also read the highly-upvoted comments. – user17732522 Jan 24 '22 at 19:51
2

The other answer is a good explanation of why you can't use function templates to deduce std::function from a function pointer. But in your comment you said your goal was to constrain the template argument, to only allow types that can be invoked with no arguments.

In c++20, concepts were introduced to make constraining template arguments easier. You can use the invocable concept to only allow types that are callable. If invocable has no arguments, it only accepts types that are callable with no arguments:

void funcCxx20(std::invocable auto fptr) {
  std::cout << "C++20 function's value is\"" << fptr() << '\"' << std::endl;
}

In c++17, you can use std::is_invocable with std::enable_if_t to accomplish the same thing:

template <typename F>
std::enable_if_t<std::is_invocable_v<F>, void> funcCxx(F&& fptr) {
  std::cout << "C++ function's value is\"" << fptr() << '\"' << std::endl;
}

In c++14, can use older form of enable_if and this solution

In both cases, you can add using V = decltype(fptr()) inside the function to deduce the return value type.

parktomatomi
  • 3,851
  • 1
  • 14
  • 18
  • Thank you! These ideas are great but I can't use them. Concepts are something a lot of my code would benefit from, but I can't use C++20 yet. Also the using declaration seems to be creating another V, as I get the error: `declaration of 'V' shadows template parameter`. – Nathan29006781 Jan 25 '22 at 04:18