#c #c 11 #stl
#c #c 11 #stl
Вопрос:
У меня есть вектор определенного пользователем типа (Student). У меня есть 2 функции, которые почти идентичны, за исключением одного вызова функции внутри них.
Вот 2 функции:
Student lowest_grade(const std::vector<Student> amp;all_students){
return *std::min_element(std::begin(all_students), std::end(all_students),
[](const Student amp;a, const Student amp;b){
return a.get_average() < b.get_average();});
}
Student highest_grade(const std::vector<Student> amp;all_students){
return *std::max_element(std::begin(all_students), std::end(all_students),
[](const Student amp;a, const Student amp;b){
return a.get_average() < b.get_average();});
}
Обе эти функции работают корректно для моего использования, но, похоже, это можно было бы легко построить лучше. Я хочу создать функцию, которую я мог бы передать либо в min_element, либо в max_element, что-то вроде:
template <typename func>
Student dispatch(const std::vector<Student> amp;all_students, func){
return *func(std::begin(all_students), std::end(all_students),
[](const Student amp;a, const Student amp;b){
return a.get_average() < b.get_average();});
}
Но мне не удается заставить это работать должным образом. Я не уверен, как это сделать.
РЕДАКТИРОВАТЬ — вот как я вызываю функцию отправки сообщение об ошибке:
std::cout<<"lowest: "<< dispatch(all_students, std::max_element);
Сообщение об ошибке:
g m.cpp -std=c 11 -Wall -o main
m.cpp: In function ‘int main()’:
m.cpp:86:63: error: missing template arguments before ‘(’ token
std::cout<<"lowest: "<< dispatch(all_students, std::function(std::max_element));
^
ryan@ryan-VirtualBox:~/Desktop/Prog/daily/167m$ make
g m.cpp -std=c 11 -Wall -o main
m.cpp: In function ‘int main()’:
m.cpp:86:81: error: no matching function for call to ‘dispatch(std::vector<Student>amp;, <unresolved overloaded function type>)’
std::cout<<"lowest: "<< dispatch<std::function>(all_students, std::max_element);
^
m.cpp:86:81: note: candidate is:
m.cpp:71:9: note: template<class func> Student dispatch(const std::vector<Student>amp;, func)
Student dispatch(const std::vector<Student> amp;all_students, func){
^
m.cpp:71:9: note: template argument deduction/substitution failed:
Комментарии:
1. Не могли бы вы подробнее рассказать о том, как это не работает? Особенно, пожалуйста, покажите, как вы используете свою
dispatch
функцию.2. В случае, если вы одновременно вычисляете min и max, примите во внимание std::minmax_element .
Ответ №1:
Это сделает это:
template <typename func>
Student dispatch(const std::vector<Student> amp;all_students, const funcamp; fn){
return *fn(std::begin(all_students), std::end(all_students),
[](const Student amp;a, const Student amp;b){
return a.get_average() < b.get_average();});
}
Параметр шаблона — это просто тип чего-то.
Я бы посоветовал быть осторожным и никогда не вызывать этот метод с пустым вектором, потому что он вызовет исключение при разыменовании пустого итератора. Лучше было бы:
template <typename func>
Student dispatch(const std::vector<Student> amp;all_students, const funcamp; fn){
auto it = fn(std::begin(all_students), std::end(all_students),
[](const Student amp;a, const Student amp;b){
return a.get_average() < b.get_average();});
if (it != all_students.end()) {
return *it;
}
// Some exception handling, because returning an instance of student is not possible.
}
Еще одно предложение — отсортировать учащихся перед использованием данных. Тогда вы также сможете получить другие статистические данные, такие как медиана.
std::sort(all_students.begin(), all_students.end() [](const Student amp;a, const Student amp;b){return a.get_average() < b.get_average();});
Самый низкий студент — это первый элемент, а самый высокий — последний. Это также не позволит вам создавать исключения.
Существует еще одна проблема с вашим вызовом. Вам нужно вызвать dispatch, например:
dispatch(all_students, std::max_element<std::vector<Student>::const_iterator, std::function<bool(const Student amp;, const Student amp;)>>);
STL не выполняет никакой дедуктивной магии и не может самостоятельно решить, какая max_element
функция вам нужна. Поэтому вы должны указать его.
Комментарии:
1. Я должен был быть более ясным, я понимаю, что сортировка, вероятно, является лучшим вариантом, однако это всего лишь небольшая демонстрационная программа для работы над изучением C11.
Ответ №2:
std::max_element
это шаблонная функция, и компилятор не может определить тип шаблона, необходимый таким образом.
Вы можете использовать следующее, чтобы определить, какой прототип вы хотите:
// Your lambda as functor
struct CompAverage
{
bool operator () (const Student amp; a, const Student amp; b) const
{
return a.get_average() < b.get_average();
}
};
using Student_IT = std::vector<Student>::const_iterator;
Student dispatch(const std::vector<Student> amp;all_students,
Student_IT (*f)(Student_IT, Student_IT, CompAverage))
{
return *f(std::begin(all_students), std::end(all_students), CompAverage{});
}
int main()
{
std::vector<Student> v(42);
dispatch(v, amp;std::min_element);
dispatch(v, amp;std::max_element);
return 0;
}
Ответ №3:
Мой предпочтительный способ сделать это — обернуть алгоритм в лямбда-выражение, а затем передать лямбда-выражение в функцию шаблона. Он имеет приятный синтаксис, когда вы оборачиваете его в макрос:
#define LIFT(...)
([](autoamp;amp;... args) -> decltype(auto) {
return __VA_ARGS__(std::forward<decltype(args)>(args)...);
})
template <typename Func>
Student dispatch(const std::vector<Student> amp;all_students, Func func){
return *func(std::begin(all_students), std::end(all_students),
[](const Student amp;a, const Student amp;b){
return a.get_average() < b.get_average();});
}
// ...
std::cout<<"lowest: "<< dispatch(all_students, LIFT(std::max_element));
Комментарии:
1. Хотя я не поклонник defines, это приятная магия 🙂
2. Это выглядит устрашающе знакомым. При соответствующем оборудовании вы также можете использовать
ordered_by (MFLIFT (get_average)))
вместо лямбда-выражения.
Ответ №4:
Ваша функция может быть написана так, как вы хотите, следующим образом:
template<typename Func>
Student dispatch(const std::vector<Student> amp;all_students, Func func)
{
assert(!all_students.empty());
return *func(std::begin(all_students), std::end(all_students),
[](const Student amp;a, const Student amp;b){
return a.get_average() < b.get_average();});
}
И вызывается как
dispatch(students,
std::min_element<decltype(students)::const_iterator,
bool(*)(const Studentamp;, const Studentamp;)>);
dispatch(students,
std::max_element<decltype(students)::const_iterator,
bool(*)(const Studentamp;, const Studentamp;)>);
Вы можете немного сократить детализацию, если реализуете operator<
for Student
. Это позволит вам опустить аргумент шаблона для компаратора.
template<typename Func>
Student dispatch(const std::vector<Student> amp;all_students, Func func)
{
assert(!all_students.empty());
return *func(std::begin(all_students), std::end(all_students));
}
dispatch(students,
std::min_element<decltype(students)::const_iterator>);
dispatch(students,
std::max_element<decltype(students)::const_iterator>);
Еще один способ сделать это — всегда вызывать min_element
внутри диспетчеризации, но передавать компараторы с другим поведением.
template<typename Comparator>
Student dispatch(const std::vector<Student> amp;all_students, Comparator comp)
{
assert(!all_students.empty());
return *std::min_element(std::begin(all_students), std::end(all_students),
comp);
}
dispatch(students, std::less<Student>());
dispatch(students, std::greater<Student>()); // requires operator> for Student
Наконец, если вы всегда собираетесь извлекать как самые низкие, так и самые высокие оценки, стандартная библиотека предлагает std::minmax_element
, которые будут извлекать оба в одном вызове.
auto minmax = std::minmax_element(std::begin(students), std::end(students));
Живая демонстрация всех различных опций.