Passing the STL algorithm to another function

I have a vector of a certain type (Student). I have two functions that are almost identical, with the exception of one function call inside them.

Here are 2 functions:

Student lowest_grade(const std::vector<Student> &all_students){ return *std::min_element(std::begin(all_students), std::end(all_students), [](const Student &a, const Student &b){ return a.get_average() < b.get_average();}); } Student highest_grade(const std::vector<Student> &all_students){ return *std::max_element(std::begin(all_students), std::end(all_students), [](const Student &a, const Student &b){ return a.get_average() < b.get_average();}); } 

Both of these functions work correctly for my use, but it looks like it could easily be built better. I want to create a function that I could pass to either min_element or max_element, for example:

 template <typename func> Student dispatch(const std::vector<Student> &all_students, func){ return *func(std::begin(all_students), std::end(all_students), [](const Student &a, const Student &b){ return a.get_average() < b.get_average();}); } 

But I can not get this to work correctly. I am not sure how to do this.

EDIT is how I call the submit function + error message:

 std::cout<<"lowest: "<< dispatch(all_students, std::max_element); 

Error message:

 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>&, <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>&, func) Student dispatch(const std::vector<Student> &all_students, func){ ^ m.cpp:71:9: note: template argument deduction/substitution failed: 
+6
source share
4 answers

Your function can be written as you want:

 template<typename Func> Student dispatch(const std::vector<Student> &all_students, Func func) { assert(!all_students.empty()); return *func(std::begin(all_students), std::end(all_students), [](const Student &a, const Student &b){ return a.get_average() < b.get_average();}); } 

And is called as

 dispatch(students, std::min_element<decltype(students)::const_iterator, bool(*)(const Student&, const Student&)>); dispatch(students, std::max_element<decltype(students)::const_iterator, bool(*)(const Student&, const Student&)>); 

You can slightly reduce the amount of text if you implement operator< for Student . This will allow you to omit the template argument for the comparator.

 template<typename Func> Student dispatch(const std::vector<Student> &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>); 

Another way to do this is to always call min_element in dispatch, but pass in comparators with different behaviors.

 template<typename Comparator> Student dispatch(const std::vector<Student> &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 

Finally, if you always get both the lowest and highest grades, the standard library offers std::minmax_element , which will be retrieved both in one call.

 auto minmax = std::minmax_element(std::begin(students), std::end(students)); 

Live demonstration of all the various parameters.

+1
source

This will be done:

 template <typename func> Student dispatch(const std::vector<Student> &all_students, const func& fn){ return *fn(std::begin(all_students), std::end(all_students), [](const Student &a, const Student &b){ return a.get_average() < b.get_average();}); } 

The template parameter is just a type of something.

I would suggest cautiously calling this method a never empty vector, because it will throw an exception when dereferencing an empty iterator. It would be better if:

 template <typename func> Student dispatch(const std::vector<Student> &all_students, const func& fn){ auto it = fn(std::begin(all_students), std::end(all_students), [](const Student &a, const Student &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. } 

Another suggestion is to sort students before using the data. Then you can also get other statistics, such as the median.

 std::sort(all_students.begin(), all_students.end() [](const Student &a, const Student &b){return a.get_average() < b.get_average();}); 

The lowest student is the first element, and the highest is the last. It will also prevent you from creating exceptions.

Another problem with your call. You need to call send, for example:

 dispatch(all_students, std::max_element<std::vector<Student>::const_iterator, std::function<bool(const Student &, const Student &)>>); 

STL does not do the magic of deduction and cannot decide for itself which max_element function you need. Therefore you need to specify it.

+3
source

std::max_element is a template function, and the compiler cannot infer the required type of template in this way.

You can use the following to force the prototype you want:

 // Your lambda as functor struct CompAverage { bool operator () (const Student & a, const Student & b) const { return a.get_average() < b.get_average(); } }; using Student_IT = std::vector<Student>::const_iterator; Student dispatch(const std::vector<Student> &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, &std::min_element); dispatch(v, &std::max_element); return 0; } 

Living example

+2
source

My preferred way to do this is to wrap the algorithm in a lambda and then pass the lambda to a template function. It has good syntax when you wrap it in a macro:

 #define LIFT(...) \ ([](auto&&... args) -> decltype(auto) { \ return __VA_ARGS__(std::forward<decltype(args)>(args)...); \ }) template <typename Func> Student dispatch(const std::vector<Student> &all_students, Func func){ return *func(std::begin(all_students), std::end(all_students), [](const Student &a, const Student &b){ return a.get_average() < b.get_average();}); } // ... std::cout<<"lowest: "<< dispatch(all_students, LIFT(std::max_element)); 
+2
source

Source: https://habr.com/ru/post/971407/


All Articles