Bee*_*ope 10 c++ comparison c++11
我经常发现自己想为a创建一个比较器对象,struct或者class只是提取一个类的成员并对其进行常规<比较.
例如:
struct student {
int id;
std::string name;
};
// sort by ID
std::sort(students.begin(), students.end(), [](const student& l, const student& r){ return l.id < r.id; });
Run Code Online (Sandbox Code Playgroud)
那里有很多样板,特别是因为我们必须重复声明l和r.标准库中是否有一种方法可以基于"提取器"函数创建比较器,该函数返回要比较的对象?
就像是:
std::sort(students.begin(), students.end(), compare_on([](const student& s){ return s.id; });
Run Code Online (Sandbox Code Playgroud)
我使用C++ 11的,但也有兴趣,如果有在以后的标准解决方案,不适用于C++ 11(所以我可以补充一下我的"的理由升级"列表).
我在这里要求使用单个成员作为合并,以及默认比较"小于",但是对于易于构成的技术的奖励点,例如允许您在字典顺序中使用两个字段,或者更改比较运算符.
您正在寻找的是允许将投影传递到算法中.N4128为标准库提出了这个建议,而C++ 20将为许多算法提供这些.
但在那之前,我们可以自己做.写一个新的重载sort:
struct identity {
template <typename T>
T&& operator()(T&& t) const noexcept { return std::forward<T>(t); }
};
// because no std::less<> in C++11 yet
struct less {
template <typename T, typename U>
constexpr bool operator()(T const& lhs, U const& rhs) const {
return lhs < rhs;
}
};
template <typename Range, typename Comp=less, typename Proj=identity>
void sort_proj(Range& range, Comp comp={}, Proj proj={}) {
using std::begin;
using std::end;
auto first = begin(range), last = end(range);
using reference = typename std::iterator_traits<decltype(first)>::reference;
std::sort(first, last,
[&](reference lhs, reference rhs) {
return comp(std::ref(proj)(lhs), std::ref(proj)(rhs));
});
}
Run Code Online (Sandbox Code Playgroud)
std::ref(f)(x)是一个INVOKE在C++ 11中获得功能的技巧.它基本上允许您将指针作为投影传递给成员.这个实现可以让你写:
sort_proj(students, less{}, &student::id);
Run Code Online (Sandbox Code Playgroud)
请注意,投影与排序无关.所以我可以很容易地做各种事情:
sort_proj(students); // sort on students, if they're ordered
sort_proj(students, greater{}, &student::name); // decreasing, by name
sort_proj(students, less{}, // by name, then id
[](student const& s) {
return std::tie(s.name, s.id);
});
Run Code Online (Sandbox Code Playgroud)
这种做法是超级消除了大量的样板,从一般的算法是有用的.我有一个标题,它充满了许多常用标准算法的基于投影的重载.
您可以定义一个实用程序类
template <class Fct> class compare_on {
public:
compare_on(Fct&& get) : get(std::forward<Fct>(get)) {}
template <class T> bool operator()(const T& lhs, const T& rhs)
{
return get(lhs) < get(rhs);
}
private:
Fct get;
};
Run Code Online (Sandbox Code Playgroud)
然后将其传递给std::sort就像您描述的那样(使用 C++17 类模板参数推导)
std::sort(students.begin(), students.end(),
compare_on([](const student& s){ return s.id; }));
Run Code Online (Sandbox Code Playgroud)
从 C++17 开始,@Justin 指出实际比较可以改进的评论(使用#include <functional>),这样
return std::invoke(get, lhs) < std::invoke(get, rhs);
Run Code Online (Sandbox Code Playgroud)
它允许使用数据成员引用进行实例化:
std::sort(students.begin(), students.end(), compare_on(&student::id));
Run Code Online (Sandbox Code Playgroud)
当绑定到 C++11 时,忘记std::invoke并使用compare_on. 后者不适合 lambda,因此通常使用参数推导make_*助手:
template <class Fct> auto make_compare_on(Fct&& get)
-> decltype(compare_on<Fct>(std::forward<Fct>(get)))
{
return compare_on<Fct>(std::forward<Fct>(get));
}
Run Code Online (Sandbox Code Playgroud)
请注意,您可以删除 C++14 中的尾随返回类型。
最后一点,这里的命名应该改进:compare_on具有误导性,因为它隐藏了函数对象真正的作用 - 通过operator <. 也许compare_less_then或类似的东西会更好,或者添加另一个可以指定为标准谓词之一的模板参数(std::less等等)。
| 归档时间: |
|
| 查看次数: |
198 次 |
| 最近记录: |