1

This is my code:

#include<iostream> struct Item { int val; }; struct XItem { int val; }; void transform(const Item &i, XItem &target) { target.val = i.val; } template<typename T> void show(const T &v) { std::cout << v.val << std::endl; } template<typename ParamsType, typename ResultType> void handleRequest(Item &cur, ResultType (*impl)(const ParamsType &p)) { ParamsType p{}; transform(cur, p); ResultType res = (*impl)(p); show(res); } struct ResItem { int val; }; int main(int argc, char *argv[]) { Item i{42}; handleRequest(i, [](const XItem &x) { return ResItem{x.val}; }); return 0; } 

Compiling it gives the following error:

test.cpp:33:3: error: no matching function for call to 'handleRequest' handleRequest(i, [](const XItem &x) { ^~~~~~~~~~~~~ test.cpp:21:6: note: candidate template ignored: could not match 'ResultType (*)(const ParamsType &)' against '(lambda at test.cpp:33:20)' void handleRequest(Item &cur, ResultType (*impl)(const ParamsType &p)) { ^ 

I am unsure why this happens, however I do suspect that because the lambda, while being implicitly convertible to a function pointer, isn't one, the template parameters can't be deduced from it.

I tried using std::function<ResultType(const ParamsType &p)> instead, which also doesn't work. This question details the problem, so I tried to use its solution:

template<typename ParamsType, typename ResultType, typename Callback> void handleRequest(Item &cur, Callback cb) { ParamsType p = transform(cur); ResultType res = std::invoke(cb, p); show(res); } 

However, now ParamsType and ResultType cannot be implicitly deduced from the callback, I would need to give them explicitly. But I really want to infer ResultType because in the actual code, it can be quite lengthy (it is inferred from the lambda return statement, which is more complex than in this minimal example). I need the two types because both transform and show are overloaded and need the types to bind.

Is it possible to have handleRequest infer those types in this scenario and if so, how?

9
  • What about auto p = transform(cur); auto res = cb(p);? Commented Aug 12, 2020 at 11:12
  • If you need the types in the context of the function you can additionally use decltype Commented Aug 12, 2020 at 11:16
  • @super In actual code, I would have multiple transforms like A transform(const Item&) {…}, B transform(const Item&) {…} which would be illegal. Commented Aug 12, 2020 at 11:27
  • In that case extracting the types with a type-trait is the more idiomatic approch. Keeps the code more generic then requiring a function pointer to be passed. Especially since that limits you to lambdas without capture. Commented Aug 12, 2020 at 11:45
  • @super Mind that the body of handleRequest is vastly simplified. In actual code, transform(cur, p); is cur.get_to(p); from this json library which limits my possibilities. I gave the body to show how I use the types, not for getting advice of how not to use them there, I am sorry if that wasn't clear. Commented Aug 12, 2020 at 12:00

1 Answer 1

2

The problem is, implicit conversion (from lambda to function pointer) is not considered in template argument deduction, which fails deducing the template parameters.

Type deduction does not consider implicit conversions (other than type adjustments listed above): that's the job for overload resolution, which happens later.

You can specify the template arguments explicitly to bypass the deduction,

handleRequest<XItem, ResItem>(i, [](const XItem &x) { return ResItem{x.val}; }); 

Or convert the lambda to function pointer explicitly,

handleRequest(i, static_cast<RestItem(*)(const XItem&)>([](const XItem &x) { return ResItem{x.val}; })); 

Or use the operator+ to convert the lambda to function pointer.

handleRequest(i, +[](const XItem &x) { return ResItem{x.val}; }); 
Sign up to request clarification or add additional context in comments.

3 Comments

operator+ is exactly what I was looking for since I don't need to give the types explicitly.
@flyx You might want to refer to this post about how operator+ magic does. And note that only non-capture lambda could covnert to function pointer.
Thanks. Using only non-capture lambdas is perfectly viable for my use-case.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.