C++: функция шаблона, принимающая функцию, которая принимает другую функцию, которая использует ссылку на тип шаблона в качестве параметра

Рассмотрим следующий код

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&))

Почему вы не используете std::find / std::find_if?

463035818_is_not_a_number 10.01.2023 14:04

Пожалуйста, не удаляйте включения из вашего кода.

463035818_is_not_a_number 10.01.2023 14:05

-> template<typename T,typename F> bool contains(std::vector<T> vec, T elem, F equivalent)

463035818_is_not_a_number 10.01.2023 14:06

Лямбда не является указателем на функцию. Его можно преобразовать в единицу, но это противоречит выводу T.

463035818_is_not_a_number 10.01.2023 14:09

Кстати, ваш заголовок кажется немного запутанным. В вашем коде нет функции, принимающей функцию, которая принимает другую функцию. Этого уровня слишком много. И вам просто нужно сделать это проще: шаблон функции, который принимает вызываемый объект.

463035818_is_not_a_number 10.01.2023 14:23
Конечные и Readonly классы в PHP
Конечные и Readonly классы в PHP
В прошлом, когда вы не хотели, чтобы другие классы расширяли определенный класс, вы могли пометить его как final.
От React к React Native: Руководство для начинающих по разработке мобильных приложений с использованием React
От React к React Native: Руководство для начинающих по разработке мобильных приложений с использованием React
Если вы уже умеете работать с React, создание мобильных приложений для iOS и Android - это новое приключение, в котором вы сможете применить свои...
БЭМ: Конвенция об именовании CSS
БЭМ: Конвенция об именовании CSS
Я часто вижу беспорядочный код CSS, особенно если проект большой. Кроме того, я совершал эту ошибку в профессиональных или личных проектах и...
Революционная веб-разработка ServiceNow
Революционная веб-разработка ServiceNow
В быстро развивающемся мире веб-разработки ServiceNow для достижения успеха крайне важно оставаться на вершине последних тенденций и технологий. По...
Как добавить SEO(Search Engine Optimization) в наше веб-приложение и как это работает?
Как добавить SEO(Search Engine Optimization) в наше веб-приложение и как это работает?
Заголовок веб-страницы играет наиболее важную роль в SEO, он помогает поисковой системе понять, о чем ваш сайт.
Конфигурация Jest в angular
Конфигурация Jest в angular
В этой статье я рассказываю обо всех необходимых шагах, которые нужно выполнить при настройке jest в angular.
1
5
50
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Это можно исправить, добавив 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 из лямбда.

Я вижу четыре возможных решения.

  1. Первый — это решение в ответе sklott: определите 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, поэтому компилятор может преобразовать лямбду в функцию или в указатель на функцию.

  1. Используйте другое имя типа шаблона (как предложено в комментарии) для функции или функционала
template <typename T, typename F>
bool contains (std::vector<T> vec, T elem, F equivalent)

  1. Вы можете оставить без изменений функцию contains(), но вы можете изменить вызов, используя оператор + перед лямбдой, чтобы принудительно преобразовать лямбда в указатель функции.
// .........................V  the '+' operator force the conversion
contains(vec, S(15,159.48), +[](S const & a, S const & b)... 

Таким образом, компилятор получает указатель на функцию, правильно выводящий S, для T, также из третьего аргумента.

  1. Вы можете оставить без изменений функцию 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. Таким образом, вывод типа не происходит, поэтому компилятор может преобразовать лямбду в функцию.

Спасибо, это отвечает почти на все, что меня интересовало!

Nicnajder 19.01.2023 17:29

Другие вопросы по теме