I'm attempting to write a function that basically transforms an instance of a class templated on one type to an instance of the same class templated on a second type. I'd like to avoid having to explicitly state the template types when calling the function.
Here's a minimal compilable example of what I'm trying to do:
template<class T> class container {};
template <class A, class B, template <class C> class Container>
Container<B> transform(Container<A> c, B(func)(A))
{
return Container<B>{};
}
int do_something(int in)
{
return in + 1;
}
int main()
{
container<int> c{};
//this one doesn't work:
//transform(c, [](int in) -> int { return in + 1; });
//these are fine:
transform<int, int>(c, [](int in) -> int { return in + 1; });
transform(c, do_something);
return 0;
}
Uncommenting out the first transform
call results in compile errors:
Visual Studio 2017:
error C2784: 'Container<B> transform(Container<A>,B (__cdecl *)(A))': could not deduce template argument for 'B (__cdecl *)(A)' from 'test::<lambda_afc081691b59f849887abca17e74b763>'
Whichever version of g++ coliru.stacked-crooked.com uses by default:
main.cpp:4:14: note: template argument deduction/substitution failed:
main.cpp:18:52: note: mismatched types 'B (*)(A)' and 'main()::<lambda(int)>'
transform(c, [](int in) -> int { return in + 1; });
^
Does this mean that it is not possible for the compiler to deduce the signature of a lambda, even when it has been clearly defined like this?
I know I can rewrite my transform function like this:
template <class A, template <class C> class Container, class F>
auto transform(Container<A> c, F func)->Container<decltype(func(A{}))>
{
return Container<decltype(func(A{}))>{};
}
but now the function signature is a little less readable and the error messages I get if I supply an inappropriate function are quite unfriendly. Using a std::function<B(A)>
doesn't help either.
Is there a way to use the more tightly specified function argument with lambdas and without explicitly adding the template types?