Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should I extend std::less for a comparison functor?

Tags:

c++

stl

c++03

I want to create a shared_ptr content-comparison functor to stand in for std::less<T> in associative containers and std algorithms. I've seen several examples of custom comparators that use the following (or similar) model:

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

But why wouldn't I want to instead extend std::less? Like so:

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

Does this buy me anything at all?

I would think this gets me the typedefs for free, as though I was extending the deprecated std::binary_function. In C++03, I actually would be extending it through std::less. However, this would also be portable from C++03 to C++11/14 and even C++17 when std::binary_function will be removed, as it just follows the changes in std::less.

I've read a bunch of answers on StackOverflow regarding std::less use, custom comparison functors, and even some of the Standard specs and proposals. I see specializations of std::less and guidance not to extend STL containers, but I can't seem to find any examples of extending std::less or guidance against it. Am I missing an obvious reason not to do this?

EDIT: Removed C++11 tag, as it is causing confusion to the answerers. I am hoping to get forward-portability, but C++03 is required. If you provide a C++11-only answer for others to use (totally fine), please note that.

like image 703
Bloodgain Avatar asked Sep 27 '22 21:09

Bloodgain


1 Answers

You can create a reusable template towards any dereferencable object (i.e. any (smart) pointer) by simply forwarding the call to std::less or any other comparable object.

// 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> {};
like image 173
Andrew Avatar answered Oct 04 '22 19:10

Andrew