Consider following code
struct S
{
unsigned u;
float f;
S(unsigned u0, float f0) : u(u0), f(f0) {}
};
template<typename T> bool contains(std::vector<T> vec, T elem, bool equivalent(const T&, const T&))
{
// some implementation
}
int main() {
std::vector<S> vec{{15, 17.8}};
std::cout << contains(vec, S(15,159.48), [](S& a, S& b) -> bool {return a.u == b.u;}) << std::endl;
}
Why this code does not compile and how to fix it? I believe that it is clear what the function contains should do from the context.
I get following error:
main.cpp:32:18: error: no matching function for call to 'contains'
std::cout << contains(vec, S(15,159.48), [](S& a, S& b) -> bool {return a.u == b.u;}) << std::endl;
^~~~~~~~
main.cpp:14:27: note: candidate template ignored: could not match 'bool (*)(const T &, const T &)' against '(lambda at main.cpp:32:46)'
template<typename T> bool contains(std::vector<T> vec, T elem, bool equivalent(const T&, const T&))
The problem is that a lambda isn't a function; it's an object with a function (
operator()) inside it.In your case, given that the lambda doesn't capture, can be converted to a function pointer.
So why the compiler doesn't convert the lambda in a function pointer? There is another problem; a sort of chicken-and-egg problem: the deduction of the
Ttemplate type.The template
contains()use the
Ttype for all the arguments: forvec, forelemand forequivalent. So the compiler try to deduceTfrom all the arguments.Given that the compiler try to deduce
Talso form the third argument (chicken and egg problem) can't deduceTfrom the lambda because the lambda ins't a function pointer and can't convert the lambda in a function pointer because doesn't deduceTfrom the lambda.I see four possible solutions.
contains(), usingstd::type_identity, as followsor also as follows
This way you inhibit the
Tdeduction fromequivalent,Tis deduced (asS) fromvecandelem, so the compiler can convert the lambda to a function or to a function pointer.This way
equivalentcan be directly a lambda, without conversion. So you can also accept a capturing lambda (that can't be converted to a function). The disadvantage is that you can't force thatequivalentreceive twoT const &arguments, but the use ofequivalentshould be enough to check inconsistenciescontains()function but you can change the call, using the+operator in front of the lambda so forcing the conversion of the lambda to a function pointerBut remember to define the lambda receiving two
S const &, as in function definition, not twoS &, as in your example.This way the compiler receive a function pointer correctly deduce
S, forT, also from the third argument.contains()function and explicit the template type in the callThis way the call explicit the
Stype, forT. So no type deduction take place, so the compiler can convert the lambda to a function