Рассмотрим следующий код
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;
}
Почему этот код не компилируется и как это исправить? Я считаю, что из контекста понятно, что должна делать функция.
Я получаю следующую ошибку:
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&))
Пожалуйста, не удаляйте включения из вашего кода.
-> template<typename T,typename F> bool contains(std::vector<T> vec, T elem, F equivalent)
Лямбда не является указателем на функцию. Его можно преобразовать в единицу, но это противоречит выводу T.
Кстати, ваш заголовок кажется немного запутанным. В вашем коде нет функции, принимающей функцию, которая принимает другую функцию. Этого уровня слишком много. И вам просто нужно сделать это проще: шаблон функции, который принимает вызываемый объект.
Это можно исправить, добавив std::type_identity_t к третьему параметру, чтобы он не участвовал в выводе параметров шаблона:
#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;
}
Если у вас нет С++ 20, вы можете легко определить его самостоятельно:
template< class T >
struct type_identity {
using type = T;
};
template< class T >
using type_identity_t = typename type_identity<T>::type;
Почему этот код не компилируется и как это исправить? Я считаю, что из контекста понятно, что должна делать функция.
Проблема в том, что лямбда — это не функция; это объект с функцией (operator()) внутри него.
В вашем случае, учитывая, что лямбда не фиксируется, ее можно преобразовать в указатель функции.
Так почему же компилятор не преобразует лямбду в указатель на функцию? Есть еще одна проблема; своего рода проблема курицы и яйца: вывод типа шаблона T.
Шаблон contains()
template <typename T>
bool contains(std::vector<T> vec, T elem, bool equivalent(const T&, const T&))
Используйте тип T для всех аргументов: для vec, для elem и для equivalent. Поэтому компилятор пытается вывести T из всех аргументов.
Учитывая, что компилятор пытается вывести T, а также сформировать третий аргумент (проблема с курицей и яйцом), он не может вывести T из лямбда, потому что лямбда не является указателем на функцию и не может преобразовать лямбда в указатель на функцию, потому что не может т вывести T из лямбда.
Я вижу четыре возможных решения.
contains()
, используя std::type_identity
, следующим образом.template <typename T>
bool contains (std::vector<T> vec, T elem, std::type_identity_t<bool(*)(const T&, const T&)> equivalent)
Или также следующим образом
template<typename T>
bool contains (std::vector<T> vec, T elem, std::type_identity_t<bool(const T&, const T&)> equivalent)
Таким образом, вы запрещаете вывод T из equivalent, T выводится (как S) из vec и elem, поэтому компилятор может преобразовать лямбду в функцию или в указатель на функцию.
template <typename T, typename F>
bool contains (std::vector<T> vec, T elem, F equivalent)
contains()
, но вы можете изменить вызов, используя оператор +
перед лямбдой, чтобы принудительно преобразовать лямбда в указатель функции.// .........................V the '+' operator force the conversion
contains(vec, S(15,159.48), +[](S const & a, S const & b)...
Таким образом, компилятор получает указатель на функцию, правильно выводящий S, для T, также из третьего аргумента.
contains()
и указать тип шаблона в вызове// .....VVV S type is explicit, no type deduction take place
contains<S>(vec, S(15,159.48), [](S const & a, S const & b)...
Таким образом, вызов явно указывает тип S для T. Таким образом, вывод типа не происходит, поэтому компилятор может преобразовать лямбду в функцию.
Спасибо, это отвечает почти на все, что меня интересовало!
Почему вы не используете std::find / std::find_if?