我应该为比较函数扩展std::less吗?
Should I extend std::less for a comparison functor?
我想创建一个shared_ptr内容比较函子来代替关联容器和std算法中的std::less<T>
。我已经看到了几个使用以下(或类似)模型的定制比较器的示例:
template <typename T>
struct SharedPtrContentsLess {
bool operator()(const boost::shared_ptr<T>& lhs,
const boost::shared_ptr<T> rhs) const {
return std::less<T>(*lhs, *rhs);
//or: return (*lhs) < (*rhs);
}
//defining these here instead of using std::binary_functor (C++11 deprecated)
typedef boost::shared_ptr<T> first_argument_type;
typedef boost::shared_ptr<T> second_argument_type;
typedef bool return_type;
};
但是为什么我不想扩展std::less
呢?像这样:
template <typename T>
struct SharedPtrContentsLess : public std::less< boost:shared_ptr<T> > {
bool operator()(const boost::shared_ptr<T>& lhs,
const boost::shared_ptr<T> rhs) const {
return std::less<T>(*lhs, *rhs);
}
};
这能给我买什么吗?
我认为这可以免费获得typedef
s,就像我扩展了已弃用的std::binary_function
一样。在c++ 03中,我实际上将通过std::less
扩展它。然而,当std::binary_function
被删除时,这也可以从c++ 03移植到c++ 11/14,甚至c++ 17,因为它只是遵循std::less
的变化。
我在StackOverflow上读了一堆关于std::less
使用,自定义比较函子,甚至一些标准规范和建议的答案。我看到std::less
的专门化和指南而不是来扩展STL容器,但我似乎找不到任何扩展std::less
或指南的例子。我是不是错过了一个不这么做的明显理由?
编辑:删除c++ 11标签,因为它会导致答案混乱。我希望得到前向可移植性,但c++ 03是必需的。如果您提供了一个仅支持c++ 11的答案供其他人使用(完全可以),请注意:
您可以通过简单地将调用转发到std::less或任何其他可比较的对象来创建针对任何可解引用对象(即任何(智能)指针)的可重用模板。
// c++11
template<template<class> Op, class T> struct deref_mixin;
template<template<class> Op, class T>
struct deref_mixin {
auto operator()(const T &l, const T &r) const
-> decltype(std::declval<Op<T>>()(*l, *r)) {
return Op<T>{}(*l, *r);
}
};
template<template<class> Op>
struct deref_mixin<Op, void> {
template<class T, class U>
auto operator()(const T &l, const U &r) const
-> decltype(std::declval<Op<T>>()(*l, *r)) {
return Op<void>{}(*l, *r);
}
};
template<class T> using less_deref = deref_mixin<std::less, T>;
template<class T> using greater_deref = deref_mixin<std::greater, T>;
template<class T> using my_comparator_deref = deref_mixin<my_comparator, T>;
// c++03
template<template<class> Op, class T>
struct deref_mixin {
bool operator()(const T &l, const T &r) const {
Op<T> op;
return op(*l, *r);
}
};
// Technically, the void template partial specialization isn't defined in c++03, but it should have been :)
template<template<class> Op>
struct deref_mixin<Op, void> {
template<class T, class U>
bool operator()(const T &l, const U &r) const {
Op<void> op;
return op(*l, *r);
}
};
template<class T> struct less_deref : deref_mixin<std::less, T> {};
正如您在问题中所说的,如果您从std::less
继承,您将获得std::less
中的三种类型。我最喜欢继承它的一点是它描述了你的意图。当我看到
struct some_non_specific_name : std::less<some_type>
我知道这是一个函子它的行为就像<
对应some_type
。我不需要读结构体来发现任何东西。
在我看来,你没有错过任何不利因素。正如你提到的,你会自动得到typedef
s。在这两种情况下都必须定义operator<
,并且在实现上没有区别。
有一件事你可能会发现,你可能会发现整洁,坏或只是不适用于你的用例(从这里到这里):std::less
的std::less<void>
专门化有一个模板operator<
为给定的参数推断operator<
的返回类型。
除非您打算使用SharedPtrContentsLess<void>
(这可能根本没有意义),否则两个解决方案将是等效的。
我会写一个deref_less
。首先,my_less
巧妙地调用std::less
:
struct my_less {
template<class Lhs, class Rhs,
class R = std::result_of_t< std::less<>( Lhs const&, Rhs const& ) >
// class R = decltype( std::declval<Lhs const&>() < std::declval<Rhs const&>() )
>
R operator()(Lhs const&lhs, Rhs const&rhs)const{
return std::less<>{}(lhs, rhs); // or lhs<rhs
}
// exact same type uses `std::less<T>`:
template<class T,
class R = std::result_of_t< std::less<>( T const&, T const& ) >
>
R operator()(T const& lhs, T const& rhs)const{
return std::less<T>{}(lhs, rhs);
}
template<class Lhs, class Rhs,
std::enable_if_t< std::is_base_of<Lhs, Rhs>{} && !std::is_same<Lhs, Rhs>{} >* = nullptr
>
bool operator()(Lhs const* lhs, Rhs const* rhs)const{
return std::less<Lhs const*>{}(lhs, rhs);
}
template<class Lhs, class Rhs,
std::enable_if_t< std::is_base_of<Rhs, Lhs>{} && !std::is_same<Lhs, Rhs>{} >* = nullptr
>
bool operator()(Lhs const* lhs, Rhs const* rhs)const{
return std::less<Rhs const*>{}(lhs, rhs);
}
template<class Lhs, class Rhs,
std::enable_if_t<
!std::is_base_of<Rhs, Lhs>{}
&& !std::is_base_of<Lhs, Rhs>{}
&& !std::is_same<Lhs, Rhs>{}
>* = nullptr
>
bool operator()(Lhs const* lhs, Rhs const* rhs)const = delete;
};
然后,deref_less
执行*
然后调用myless
:
struct deref_less {
template<class Lhs, class Rhs,
class R = std::result_of_t< my_less( decltype(*std::declval<Lhs>()), decltype(*std::declval<Rhs>()) ) >
>
R operator()(Lhs const& lhs, Rhs const&rhs)const {
return my_less{}( *lhs, *rhs );
}
};
在c++ 14中,但我使用的一切都很容易替换(std::less<>
可以替换为decltype和<
s)。
由于std::less缺乏虚析构函数(即仅隐式析构函数),从它继承可能在技术上导致未定义行为。由于这两种类型都不包含任何数据成员,因此无论对象如何被引用,析构函数都应该工作,但是标准禁止通过静态析构函数进行多态删除,因为在大多数情况下,它很有可能出现问题(切片、不完全删除)。
看这个答案:不能继承std::vector
- 如何使用std::min和std::less返回对象
- std::less是否应该允许在编译时比较不相关的指针?
- 无法获得等效的 std::less 来用于嵌套迭代器
- enable_if std的组合:: Less sizeof ..使MSVC失败
- 如何在不重载"operator()"、"std::less"、"std
- SFINAE 检查 std::less 是否有效
- 不能对 std::set<std::string, std::less 使用用户提供的比较函数<>>
- 为什么 std::less<Eigen::VectorXd> 编译失败?
- 如果 std::greater<>,那么为什么是 std::less(而不是 std::lesser<>)?
- 为什么 std::less 是一个类模板
- 在默认参数中使用依赖范围作为 std::less 的类型时出现编译错误
- 如何在包含类的向量中使用 std::less
- 无法实例化binary_function std :: Less / std ::更大
- 委托条件为std::less或std::great
- Clang, std::shared_ptr and std::less/operator<
- 使用模板调用 std::less:<int>:operator() 导致分段错误
- 如何导入 std::less<T> 作为字体名?
- std::less:错误 C2661:'std::less<_Ty>::less':没有重载函数需要 2 个参数
- 如何将比较器 a-la std::less 传递给 C 样式 Qsort
- 为什么 std::less 是一个函子