2014-06-27 27 views
6

Tôi có một vectơ thuộc kiểu người dùng được xác định (Sinh viên). Tôi có 2 chức năng gần như giống hệt nhau ngoại trừ một cuộc gọi hàm duy nhất bên trong chúng.Chuyển thuật toán STL sang một hàm khác

Đây là 2 chức năng:

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();}); 
} 

Cả hai chức năng đang làm việc một cách chính xác để sử dụng của tôi, nhưng nó có vẻ như điều này dễ dàng có thể được xây dựng tốt hơn. Tôi muốn tạo một hàm mà tôi có thể chuyển vào hoặc min_element hoặc max_element, chẳng hạn như:

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();}); 
} 

Nhưng tôi không thể quản lý để làm việc này hoạt động bình thường. Tôi không chắc chắn làm thế nào để đi về việc này.

EDIT - Đây là cách tôi đang gọi hàm văn + thông báo lỗi:

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

Các thông báo lỗi là:

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)); 
                  ^
[email protected]:~/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: 
+0

Bạn có thể giải thích chi tiết về cách nó không hoạt động không? Đặc biệt xin vui lòng cho thấy cách bạn sử dụng hàm 'dispatch' của bạn. –

+0

Trong trường hợp bạn đang tính cả phút và tối đa cùng một lúc, hãy tính đến [std :: minmax_element] (http://en.cppreference.com/w/cpp/algorithm/minmax_element). –

Trả lời

1

Chức năng của bạn có thể được viết theo cách bạn muốn như sau:

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();}); 
} 

Và gọi là

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

Bạn có thể cắt giảm độ dài một chút nếu bạn triển khai operator< cho Student. Điều này sẽ cho phép bạn bỏ qua đối số mẫu cho trình so sánh.

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

Tuy nhiên, một cách khác để làm điều này, là phải luôn luôn gọi min_element trong công văn, nhưng vượt qua trong bộ so sánh với các hành vi khác nhau.

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 

Cuối cùng, nếu bạn luôn đi múc nước cả lớp thấp nhất và cao nhất, các thư viện chuẩn cung cấp std::minmax_element rằng sẽ lấy cả hai trong một cuộc gọi duy nhất.

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

Live demo trong tất cả các tùy chọn khác nhau.

3

này sẽ làm điều đó:

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();}); 
} 

Thông số mẫu chỉ là một kiểu gì đó.

Tôi sẽ đề nghị cẩn thận gọi phương thức này không bao giờ với một véc tơ trống, bởi vì nó sẽ tăng ngoại lệ khi dereferencing một trình lặp trống. Tốt hơn là:

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. 
} 

Một đề xuất khác là sắp xếp sinh viên trước khi sử dụng dữ liệu. Sau đó, bạn cũng sẽ có thể nhận được các dữ liệu thống kê khác như trung bình.

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

Sinh viên thấp nhất là phần tử đầu tiên và là phần tử cuối cùng cao nhất. Điều này cũng sẽ ngăn cản bạn tăng các ngoại lệ.

Có một vấn đề khác với cuộc gọi của bạn. Bạn cần phải gọi cho công văn như:

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

STL không có phép trừ và không tự quyết định chức năng mong muốn của bạn. Vì vậy, bạn phải xác định nó.

+0

Tôi cần phải rõ ràng hơn, tôi nhận ra rằng sắp xếp có thể là lựa chọn tốt nhất, tuy nhiên đây chỉ là một chương trình demo nhỏ để học về C11. –

2

std::max_element là một hàm mẫu và trình biên dịch không thể suy ra loại mẫu cần thiết theo cách đó.

Bạn có thể sử dụng sau đây để buộc mà nguyên mẫu bạn muốn:

// 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; 
} 

Live example

2

Cách ưa thích của tôi để làm điều này là quấn thuật toán vào một lambda và sau đó chuyển lambda vào hàm mẫu. Nó có cú pháp đẹp khi bạn bọc nó trong một 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)); 
+0

Mặc dù tôi không phải là một fan hâm mộ của định nghĩa, đây là một số phép thuật tốt đẹp :) –

+0

Điều này trông kỳ lạ quen thuộc. Với [máy móc thích hợp] (https://github.com/sjolsen/sjo/blob/master/lift.hh), bạn cũng có thể sử dụng 'ordered_by (MFLIFT (get_average)))' thay cho lambda. –

Các vấn đề liên quan