Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

std::set iterator with same key and different comparators

While trying to solve a problem I started thinking about this - given a user-defined class and 2 comparators for it, lets say we have 2 sets std::set<user_class,comparator_inc> and std::set<user_class,comparator_dec> where the comparators sort by increasing and decreasing value on a value in the user_class(a simple int perhaps). Here's my code:

#include <iostream>
#include <set>

using std::cout;
using std::endl;
using std::set;

struct A
{
    int val;
};
struct c_inc
{
    bool operator()(const A& first,const A& second) const
    {
        return first.val > second.val;
    }
};
struct c_dec
{
    bool operator()(const A& first,const A& second) const
    {
        return first.val < second.val;
    }
};

int main()
{
    set<A,c_inc> s1;
    set<A,c_dec> s2;

    auto x = s1.insert({1});
    cout << x.first->val << endl;

    x = s2.insert({1});
    x = s2.insert({0});

    cout << x.first->val << endl;   
}

My question is: Is it defined behavior to re-assign x to the output of insert into a set with same Key but different comparator? Is there a problem with this kind of use? Is it defined in the standard somewhere what it should be or is it implementation dependent?

Since the code compiles I think that the return type of insert in both cases is the same - is this assumption correct?

like image 263
PYA Avatar asked May 16 '18 00:05

PYA


2 Answers

I think it's implementation dependent.

Conceptually the return type of s1.insert and s2.insert are different; especially they have different iterator types, i.e. std::set<A,c_inc>::iterator and std::set<A,c_dec>::iterator. And how the std::set::iterator's type is defined is implementation-defined.

[set.overview]/2

using iterator               = implementation-defined; // see [container.requirements]
using const_iterator         = implementation-defined; // see [container.requirements]
like image 88
songyuanyao Avatar answered Nov 04 '22 10:11

songyuanyao


Technically speaking, you shouldn't rely on this.

Since the code compiles I think that the return type of insert in both cases is the same - is this assumption correct?

No, it is not. Imagine this simple example:

template<class T>
struct set {
   struct iterator { /*...*/ };
};

In this case set<int>::iterator is definitely different from set<double>::iterator.

The implementation is free to implement the iterator type as a free class though (since the iterator does not depend on the comparator), which seems to be the case in the major implementations, and is what's allowing your example.

like image 26
DeiDei Avatar answered Nov 04 '22 09:11

DeiDei