tags:

views:

503

answers:

6

Hi folks,

I'd like to write a (C++) method that returns an std::set of custom objects. I do not however want to expose the comparator used when inserting the objects, so I make it a private class. I create the set like this:

std::set<some_class, some_class_comparator> return_object;

now I want to return the set, so it has to be cast like this (implicitly when returning):

(const std::set<some_class>) return_object;

This is where the compiler complains. Is there a way to cast a mutable set with a comparator to an immutable without?

thanks a lot,

holger

+1  A: 

I don't think so. AFAIK std::set<some_class> is just shorthand for std::set<some_class, std::less<some_class> > so the set can't be converted as you want.

I suggest transferring the items into some other data structure (e.g. a vector) that will maintain keep the current items in an order without needing a comparator.

Douglas Leeder
Unfortunately I'll loose the semantic meaning of the return-type then - I want the user to know he can expect unique items in the collection.
Holger
@hp: A set expresses more than uniqueness. Even an immutable one still needs the comparator to perform find() with logarithmic complexity. That's why the comparator is an intrinsic part of the type and can't be "cast away".
Éric Malenfant
@Eric: Yeah, you are right. In STL :-). In general (a.k.a. math) a set should be unique, period -- I find it weird that a set should have an order in the first place. It's only for performance-reasons and those should be hidden from the user where possible.
Holger
@hp: Defining unicity (or, more stl-ishingly, "non-equivalence") needs some sort of comparison function. So one could imagine a "set_with_unicity_and_no_compromise_to_efficiency<>", but it would still require the definition of what "unicity" means.
Éric Malenfant
+4  A: 

Is there a way to cast a mutable set with a comparator to an immutable without?

No because

std::set<some_class, some_class_comparator>

and

std::set<some_class>

are different, unrelated types entirely. Templates are just that, templates -- a way of specifying how a type is to be generated before compilation. In the second case, the comparator is the default comparer that a std::set would come with.

Is there a compelling reason that some_class_comparotor needs to be private? Could it be its own stand-alone entity? Is it just you don't want to pollute the public interface of your class?

Doug T.
Right, but they could be generated (implemented) with a cast-operator to an intuitively covariant type
Holger
They're not really covariant.
MSalters
A: 

This is two different types std::< Type, Comparator > and std::set< Type, DefaultComparator >.

bb
A: 

You want to hide an implementation detail from the caller, that doesn't sound like a template, that sounds like virtual functions. I think first, you need to ask yourself, does this really matter? If so, hide your class behind an IOpaqueSet interface. Of course, then no other STL operations could work on it.

Sanjaya R
As long as IOpaqueSet implements iterators, all STL operations can work on it. Cf. std::set itself, that's usually an opaque wrapper for a red-black tree. You can still iterate over the set, even though you can't iterate over the RB tree.
MSalters
+1  A: 

Your best solution is something like this:

class MyClass
{
    class some_class_comparator;
public:
    typedef std::set<int, some_class_comparator> ReturnSet;
    ReturnSet myMethod();
    // ...
private:
    class some_class_comparator
    {
    public:
     bool operator< (MyClass& m) {return true;}
    };
};

//later...
MyClass::ReturnSet s = my_class_instance.myMethod();
rlbond
Can you do this? Do you need the destructor to be public? I think you can do this, like you say - I just don't know for sure it'd work.
Matt Cruikshank
It works. Of course, you can then work around this by using MyClass::ReturnSet::key_compare. But if a programmer wants to get around your encapsulation by being tricky, he generally can.
rlbond
A: 

The STL comes with a lot of built-in classes, but it's also an extensible framework. If you define your::own::immutable_set, with iterators defined, other STL algorithms can use it. immutable_set<T> would be defined as a thin const wrapper around a mutable std::set<T, comparator<T> > (some template magic required to support different comparators, as they lead to different types). immutable_set<T>::iterator::operator++ would forward to std::set<T>::iterator::operator++, etc.

MSalters