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&))
CodePudding user response:
It can be fixed by adding std::type_identity_t
for 3rd parameter, so that it will not participate in deducing template parameters:
#include <iostream>
#include <vector>
#include <type_traits>
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, std::type_identity_t<bool (*)(const T&, const T&)> equivalent)
// Can also use following synaxis: std::type_identity_t<bool(const T&, const T&)> *equivalent
{
// some implementation
}
int main() {
std::vector<S> vec{{15, 17.8}};
std::cout << contains(vec, S(15,159.48), [](const S& a, const S& b) -> bool {return a.u == b.u;}) << std::endl;
}
If you don't have C 20 you can easily define it youself:
template< class T >
struct type_identity {
using type = T;
};
template< class T >
using type_identity_t = typename type_identity<T>::type;
CodePudding user response:
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.
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()
template <typename T>
bool contains(std::vector<T> vec, T elem, bool equivalent(const T&, const T&))
use the T
type for all the arguments: for vec
, for elem
and for equivalent
. So the compiler try to deduce T
from all the arguments.
Given that the compiler try to deduce T
also form the third argument (chicken and egg problem) can't deduce T
from the lambda because the lambda ins't a function pointer and can't convert the lambda in a function pointer because doesn't deduce T
from the lambda.
I see four possible solutions.
- The first one is the solution in the sklott answer: define
contains()
, usingstd::type_identity
, as follows
template <typename T>
bool contains (std::vector<T> vec, T elem, std::type_identity_t<bool(*)(const T&, const T&)> equivalent)
or also as follows
template<typename T>
bool contains (std::vector<T> vec, T elem, std::type_identity_t<bool(const T&, const T&)> equivalent)
This way you inhibit the T
deduction from equivalent
, T
is deduced (as S
) from vec
and elem
, so the compiler can convert the lambda to a function or to a function pointer.
- Use another template typename (as suggested in a comment) for a the function or functional
template <typename T, typename F>
bool contains (std::vector<T> vec, T elem, F equivalent)
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 that equivalent
receive two T const &
arguments, but the use of equivalent
should be enough to check inconsistencies
- You can leave unmodified the
contains()
function but you can change the call, using the
// .........................V the ' ' operator force the conversion
contains(vec, S(15,159.48), [](S const & a, S const & b)...
But remember to define the lambda receiving two S const &
, as in function definition, not two S &
, as in your example.
This way the compiler receive a function pointer correctly deduce S
, for T
, also from the third argument.
- You can leave unmodified the
contains()
function and explicit the template type in the call
// .....VVV S type is explicit, no type deduction take place
contains<S>(vec, S(15,159.48), [](S const & a, S const & b)...
This way the call explicit the S
type, for T
. So no type deduction take place, so the compiler can convert the lambda to a function