4

I created a templated function of which I am trying to automatically deduce the template argument. MCVE(compile it):

template<class Value, class Allocator> void foo(const std::vector<Value, Allocator>& v, const std::function<void(const Value&)>& f) { } int main() { vector<int> v; foo<int>(v, [](const int&){}); //okay //foo(v, [](const int&){}); //not okay return 0; } 

I first thought the Allocator could not be deduced but that does not seem to solve it. My next guess is it has something to do with the lambda to std::function but no idea on further steps there. Anybody got any clues about what I need to do to make this deducible?

Ps: I know "const int&" could become "int" but in the real code there is a non scalar data type there.

7
  • 2
    You might get rid of std::function and have template<class Value, class Allocator, typename Func> void foo(const std::vector<Value, Allocator>& v, Func&& f) { } Commented May 14, 2020 at 9:16
  • Please note that std::vector might have more than 2 template arguments and your code fails to take that into account. Commented May 14, 2020 at 9:17
  • @rubenvb what do you mean, looking at en.cppreference.com/w/cpp/container/vector I only see implementations with 2 template arguments. Commented May 14, 2020 at 9:27
  • @Jarod42 good suggestion but I also quite like it that the signature shows what arguments should be passed to the function/lambda. Commented May 14, 2020 at 9:34
  • C++20 should probably have concept for that :-) Commented May 14, 2020 at 9:36

1 Answer 1

3

Template argument deduction happens before implicit conversion of Lambda to std::fucntion.

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

Meaning type deduction of the template parameter Value on the 2nd function argument f fails because the implicit conversion from lambda to std::function are not considered.

As you showed you can specify template arguments explicitly (to bypass the template argument deduction). Or you can exclude the 2nd argument from deduction by using std::type_identity to declare it as non deduced context.

The nested-name-specifier (everything to the left of the scope resolution operator ::) of a type that was specified using a qualified-id:

e.g.

template<class Value, class Allocator> void foo(const std::vector<Value, Allocator>& v, const std::function<void(const std::type_identity_t<Value>&)>& f) { } 

LIVE

PS: std::type_identity is supported from C++20; if the compiler you're using doesn't support it it's not hard to make one.

Sign up to request clarification or add additional context in comments.

6 Comments

Gonna take a while to understand why this works but hopefully en.cppreference.com/w/cpp/language/… helps. Thanks.
So do I understand correctly that std::type_identity<Value>::Type is what makes the 2nd Value argument to the left of the scope resolution operator :: and thus non deduced context?
@turoni Yes. Then Value would be only deduced on the 1st function argument and anything is fine.
std::type_identity is excellent in its simplicity. I must have written tons of complicated solutions that could have been simplified using a helper like that. Note to self: Now, remember this one.
@turoni You're defining type in type_identity as private; make it public. :) godbolt.org/z/7_DXBt
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.