【问题标题】:Passing STL algorithm to another function将 STL 算法传递给另一个函数
【发布时间】:2014-06-27 06:46:52
【问题描述】:

我有一个用户定义类型(学生)的向量。我有 2 个函数,它们几乎相同,只是其中有一个函数调用。

这是两个功能:

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

这两个函数都可以正常使用,但似乎可以很容易地构建得更好。我想创建一个可以传入 min_element 或 max_element 的函数,例如:

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

但我无法让它正常工作。我不知道该怎么做。

编辑 - 这就是我调用调度函数 + 错误消息的方式:

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>&, <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:

【问题讨论】:

  • 您能否详细说明它是如何不起作用的?特别是请展示您如何使用dispatch 功能。
  • 如果您同时计算最小值和最大值,请考虑std::minmax_element

标签: c++ c++11 stl


【解决方案1】:

这样就可以了:

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

模板参数只是某种东西的一种类型。

我建议小心不要使用空向量调用此方法,因为它会在取消引用空迭代器时引发异常。更好的是:

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

另一个建议是在使用数据之前对学生进行排序。然后您还可以获取其他统计数据,例如中位数。

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

最低的学生是第一个元素,最高的学生是最后一个。这也将防止您引发异常。

您的电话还有另一个问题。你需要像这样调用调度:

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

STL 没有演绎魔法,也无法自行决定您想要哪个max_element 函数。所以你必须指定它。

【讨论】:

  • 我应该更清楚一点,我意识到排序可能是最好的选择,但这只是一个用于学习 C11 的小型演示程序。
【解决方案2】:

std::max_element 是一个模板函数,编译器无法通过这种方式推断出所需的模板类型。

您可以使用以下命令来强制使用您想要的原型:

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

【讨论】:

    【解决方案3】:

    我首选的方法是将算法包装到 lambda 中,然后将 lambda 传递给模板函数。当你将它包装在一个宏中时,它有很好的语法:

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

    【讨论】:

    • 虽然我不喜欢定义,但这是一个很好的魔法:)
    • 这看起来非常熟悉。使用appropriate machinery,您还可以使用ordered_by (MFLIFT (get_average))) 代替lambda。
    【解决方案4】:

    您的函数可以按照您想要的方式编写如下:

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

    并调用为

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

    如果为Student 实现operator&lt;,则可以大大减少冗长。这将允许您省略比较器的模板参数。

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

    另一种方法是在调度中始终调用min_element,但传入具有不同行为的比较器。

    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
    

    最后,如果您总是要同时获取最低和最高成绩,标准库提供了std::minmax_element,可以在一次调用中同时获取两者。

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

    Live demo 的所有不同选项。

    【讨论】:

      猜你喜欢
      • 2015-08-01
      • 1970-01-01
      • 2019-07-25
      • 2012-09-25
      • 2020-10-03
      相关资源
      最近更新 更多