5

In such a code:

Comparator comp(3);

set<string, Comparator> s1(comp);
set<string, Comparator> s2(comp);
set<string, Comparator> s3(comp);
set<string, Comparator> s4(comp); 

the actual instance of the Comparator (namely comp) is copied at each creation of a set object as the cpp reference states

The container keeps an internal copy of alloc and comp, which are used to allocate storage and to sort the elements throughout its lifetime.

So we were wondering if this is legal in C++

#include <set>
#include <iostream>

struct A {
    int i = 0;
    bool operator()(int a, int b)
    {
        ++i;
        return a < b;
    }
};

int main()
{    
    A a;
    std::set<int, A&> s1( {1, 2, 3}, a);
    std::set<int, A&> s2( {4, 5, 6}, a);
    std::cout << a.i;
}

Thanks in advance.

jrok
  • 54,456
  • 9
  • 109
  • 141
Issam T.
  • 1,677
  • 1
  • 14
  • 32
  • _'So we were wondering if this is legal in C++'_ At least it [compiles fine](http://ideone.com/O85lFk), and does what's expected IMHO. – πάντα ῥεῖ Apr 08 '14 at 16:26
  • 1
    @πάνταῥεῖ: That's not a valid argument at all in C++ – Sebastian Mach Apr 08 '14 at 16:27
  • 1
    My feeling says it is a fragile solution; comparators are typically expected to be stateless or to externalize their state (like e.g. an pimpl does); I am not sure what the standard says, i.e. if a set is required to store its copy of the comparator or to honor that it's a reference. Unfortunately, I lack the time at this moment to research it. – Sebastian Mach Apr 08 '14 at 16:30
  • @phresnel But at least a good hint usually ;) ... Mentioning intended statelessness hit's the nail. – πάντα ῥεῖ Apr 08 '14 at 16:31
  • I feel pretty certain this isn't legal... Doesn't the comparitor type have to be copiable at least? [Edit: spec says it has to be swappable, are references swappable?] – Mooing Duck Apr 08 '14 at 16:51
  • @Mooing Duck I read that part as the compare has to be swappable only if you care to swap the container itself. – Mark B Apr 08 '14 at 17:33
  • @MarkB: You're right, I overlooked the (quite obvious) context. – Mooing Duck Apr 08 '14 at 17:45

1 Answers1

4

I'm unable to find wording in the standard forbidding using a reference type as the comparison function. Thus it seems that this would be legal. Note that some things, such as default constructing such a set, will be forbidden because your comparison type is not default constructable.

Finally note that the canonical C++ approach is to not do this, but to maintain the state externally. When you take that approach it's totally clear what you're doing and guaranteed to be safe:

#include <set>
#include <iostream>

struct A {
    int& i_;
    explicit A(int& state) : i_(state) { }
    bool operator()(int a, int b)
    {
        ++i_;
        return a < b;
    }
};

int main() {
    int i;
    std::set<int, A> s1( {1, 2, 3}, A(i));      
    std::set<int, A> s2( {4, 5, 6}, A(i));        
    std::cout << i << endl;
}
Issam T.
  • 1,677
  • 1
  • 14
  • 32
Mark B
  • 95,107
  • 10
  • 109
  • 188