我应该为比较仿函数扩展 std::less 吗?
Should I extend std::less for a comparison functor?
我想创建一个 shared_ptr 内容比较仿函数来代替关联容器和标准算法中的 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
,就好像我在扩展已弃用的 std::binary_function
。在 C++03 中,我实际上 会 通过 std::less
扩展它。然而,这也可以从 C++03 移植到 C++11/14,甚至当 std::binary_function
将被删除时的 C++17,因为它只是遵循 std::less
.[= 中的更改。 23=]
我在 Whosebug 上阅读了很多关于 std::less
使用、自定义比较仿函数,甚至一些标准规范和提案的答案。我看到 std::less
的专业化和指导 not 来扩展 STL 容器,但我似乎找不到任何扩展 std::less
的例子或反对它的指导。我是否缺少不这样做的明显理由?
编辑:删除了 C++11 标签,因为它会让回答者感到困惑。我希望获得前向可移植性,但需要 C++03。如果您提供一个仅适用于 C++11 的答案供其他人使用(完全没问题),请注意。
正如您在问题中所说,如果您从 std::less
继承,您将获得 std::less
中的三个 typedef。我最喜欢从它继承的是它描述了你的意图。当我看到
struct some_non_specific_name : std::less<some_type>
我就知道这是一个仿函数,它将作为 some_type
的 <
。我不必阅读结构体就可以找到任何东西。
据我所知,你没有遗漏任何劣势。正如您提到的,您会自动获得 typedef
s。 operator<
在这两种情况下都必须定义,并且其实现没有区别。
有一件事您可能会发现整洁、糟糕或不适用于您的用例(来自 here and here):std::less
[=13] 的专业化=] 有一个模板 operator<
推导出给定参数的 operator<
的 return 类型。
除非您打算使用 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 或任何其他可比较的对象来为任何可取消引用的对象(即任何(智能)指针)创建一个可重用的模板。
// 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 缺少虚拟析构函数(即仅隐式析构函数),从它继承在技术上可能会导致 未定义的行为 。由于这两种类型都不包含任何数据成员,因此无论对象如何引用,销毁都应该起作用,但标准禁止通过静态析构函数进行多态删除,因为它在大多数情况下很可能出现问题(切片,不完全删除)。
看到这个答案:
Thou shalt not inherit from std::vector
我想创建一个 shared_ptr 内容比较仿函数来代替关联容器和标准算法中的 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
,就好像我在扩展已弃用的 std::binary_function
。在 C++03 中,我实际上 会 通过 std::less
扩展它。然而,这也可以从 C++03 移植到 C++11/14,甚至当 std::binary_function
将被删除时的 C++17,因为它只是遵循 std::less
.[= 中的更改。 23=]
我在 Whosebug 上阅读了很多关于 std::less
使用、自定义比较仿函数,甚至一些标准规范和提案的答案。我看到 std::less
的专业化和指导 not 来扩展 STL 容器,但我似乎找不到任何扩展 std::less
的例子或反对它的指导。我是否缺少不这样做的明显理由?
编辑:删除了 C++11 标签,因为它会让回答者感到困惑。我希望获得前向可移植性,但需要 C++03。如果您提供一个仅适用于 C++11 的答案供其他人使用(完全没问题),请注意。
正如您在问题中所说,如果您从 std::less
继承,您将获得 std::less
中的三个 typedef。我最喜欢从它继承的是它描述了你的意图。当我看到
struct some_non_specific_name : std::less<some_type>
我就知道这是一个仿函数,它将作为 some_type
的 <
。我不必阅读结构体就可以找到任何东西。
据我所知,你没有遗漏任何劣势。正如您提到的,您会自动获得 typedef
s。 operator<
在这两种情况下都必须定义,并且其实现没有区别。
有一件事您可能会发现整洁、糟糕或不适用于您的用例(来自 here and here):std::less
[=13] 的专业化=] 有一个模板 operator<
推导出给定参数的 operator<
的 return 类型。
除非您打算使用 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 或任何其他可比较的对象来为任何可取消引用的对象(即任何(智能)指针)创建一个可重用的模板。
// 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 缺少虚拟析构函数(即仅隐式析构函数),从它继承在技术上可能会导致 未定义的行为 。由于这两种类型都不包含任何数据成员,因此无论对象如何引用,销毁都应该起作用,但标准禁止通过静态析构函数进行多态删除,因为它在大多数情况下很可能出现问题(切片,不完全删除)。
看到这个答案: Thou shalt not inherit from std::vector