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
T
template type.The template
contains()
use the
T
type for all the arguments: forvec
, forelem
and forequivalent
. So the compiler try to deduceT
from all the arguments.Given that the compiler try to deduce
T
also form the third argument (chicken and egg problem) can't deduceT
from the lambda because the lambda ins't a function pointer and can't convert the lambda in a function pointer because doesn't deduceT
from the lambda.I see four possible solutions.
contains()
, usingstd::type_identity
, as followsor also as follows
This way you inhibit the
T
deduction fromequivalent
,T
is deduced (asS
) fromvec
andelem
, so the compiler can convert the lambda to a function or to a function pointer.This way
equivalent
can 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 thatequivalent
receive twoT const &
arguments, but the use ofequivalent
should 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
S
type, forT
. So no type deduction take place, so the compiler can convert the lambda to a function