我应该为比较函子扩展std :: less吗?

问题描述 投票:5回答:5

我想创建一个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);
  }
};

这给我买了什么吗?

我认为这让我免费获得typedefs,好像我正在扩展已弃用的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答案(完全没问题),请注意。

c++ stl c++03
5个回答
1
投票

您可以通过简单地将调用转发给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> {};

1
投票

正如你在问题中所说,如果你从std::less继承,那么你将得到std::less中的三个typedef。我最喜欢继承它的是它描述了你的意图。当我看见

struct some_non_specific_name : std::less<some_type>

我知道这是一个有趣的< some_type。我没有必要阅读结构体来找出任何东西。


1
投票

据我所知,你不会错过任何劣势。如你所述,你会自动获得typedefs。必须在两种情况下定义operator<,并且其实施没有差异。

有一件事你可能会发现你可能会发现整洁,糟糕或者不适用于你的用例(来自herehere):有std::lessstd::less<void>专门化有一个模板operator<推断出operator<的返回类型给定的论点。

除非您打算使用SharedPtrContentsLess<void>(可能根本没有意义),否则两种解决方案都是等效的。


1
投票

我会写一个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)。


0
投票

因为std :: less缺少虚拟析构函数(即仅隐式析构函数),所以继承它可能在技术上导致未定义的行为。由于两种类型都不包含任何数据成员,因此无论对象如何被引用,销毁都应该有效,但标准禁止通过静态析构函数进行多态删除,因为在大多数情况下它存在很大的问题(切片,不完全删除)。

看到这个答案:Thou shalt not inherit from std::vector

© www.soinside.com 2019 - 2024. All rights reserved.