简体   繁体   中英

Compare std::set with custom comparer

I am trying to use the std::tie to implement the operator< in order to create a map of structs that contain a set. The same code without templates seem that it works. I am getting this message code from my compiler:

/usr/include/c++/4.8/bits/stl_algobase.h:888: error: no match for operator< (operand types are const SiPa<int, int> and const SiPa<int, int> )

 if (*__first1 < *__first2) ^ 

Everything compiles if I comment the myMap.insert({akey, true}); line.

Any hints?

template<class I = int, class S = int>
struct SiPa
{
    I i;
    S s;
};

template<class I = int, class S = int>
struct SiPaComparator
{
    bool operator() (const SiPa<I, S>& first, const SiPa<I, S>& second) const
    {
        return std::tie(first.i, first.s) < std::tie(second.i, second.s);
    }
};

template<class I = int, class S = int>
struct AKey
{
    typedef std::set< SiPa<I, S>, SiPaComparator<I,S> > SetType;
    SetType keySet;
    I keyI;
};

template<class I = int, class S = int>
struct AKeyComparator
{
    bool operator() (const AKey<I, S>& first, const AKey<I, S>& second) const
    {
        return std::tie(first.keySet, first.keyI) < std::tie(second.keySet, second.keyI);
    }
};

int main()
{
    AKey<int,int> akey;

    std::map<AKey<int,int>, bool, AKeyComparator<int,int>> myMap;
    myMap.insert({akey, true});
}

You need add operator< for struct SiPa, std::map require it

template<class I = int, class S = int>
struct SiPa {
  I i;
  S s;

  bool operator<(const SiPa<I, S> &ref) {
    return i < ref.i && s < ref.s;
  }
};

In general, comparators on map s and set s are stateful . When comparing two different set s or map s, there is no obvious way to pick which one to use.

So when comparing different set s and map s via < , you get std::lexographical_compare with no Compare argument, which uses < . (Note this sucks for set s of pointers to objects not from the same array)

struct order_by_tie {
  template<class Lhs, class Rhs,
    class=std::enable_if_t<
      std::is_base_of<order_by_tie, Lhs>::value
      && std::is_base_of<order_by_tie, Rhs>::value
    >
  >
  friend bool operator<(Lhs const& lhs, Rhs const& rhs) {
    return as_tie(lhs) < as_tie(rhs);
  }
};

order_by_tie is intended to be inherited from. It uses ADL (argument dependent lookup) to enable < on its descendent classes, implemented by calling the free function as_tie on each side then doing a < .

We use it as follows:

template<class I = int, class S = int>
struct SiPa:order_by_tie
{
  I i;
  S s;
  friend auto as_tie( SiPa const& self ) {
    return std::tie(self.i, self.s);
  }
};

template<class I = int, class S = int>
struct AKey:order_by_tie
{
  typedef std::set< SiPa<I, S>, SiPaComparator<I,S> > SetType;
  SetType keySet;
  I keyI;
  friend auto as_tie( AKey const& self ) {
    return std::tie(self.keySet, self.keyI);
  }
};

then

std::map<AKey<int,int>, bool> myMap;

works.

as_tie uses C++14, because the alternative is annoying. You can add a -> decltype(std::tie( blah, blah )) for C++11 (repeating yourself).

According to http://www.cplusplus.com/reference/set/set/operators/

The other operations also use the operators == and < internally to compare the elements, behaving as if the following equivalent operations were performed:

Notice that none of these operations take into consideration the internal comparison object of neither container .

So the comparaison of std::set<SiPa<I, S>, SiPaComparator<I,S>> is done with

operator < (const SiPa<I, S>&, const SiPa<I, S>&)

and not with

SiPaComparator<I, S>{}

The workaround is to define that operator < .

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM