6

I don't understand why the following works (although I'm glad that it works!):

I can define an std::set of Objects with a custom comparator. This custom comparator works by comparing some member variable of the two Objects being compared. BUT then I can use set's .find(x) function with x being of the member variable type rather than an object itself, and it works!

Here is an extremely simplified example:

my_class.h

class my_class   //just hold an integer
{
    public:
      int an_int;

    my_class(int new_int) : an_int(new_int)
    { }


    //compare instances of this class by comparing their integers...
    struct compare_instances   
    {    
      bool operator() (const my_class &a, const my_class &b) const
      {
        return a.an_int < b.an_int;
      }
    };
};

main.cpp:

...
std::set<my_class, my_class::compare_instances> my_class_set;
my_class_set.insert( my_class(18) );
my_class_set.insert( my_class(7)  );
my_class_set.insert( my_class(22) );

std::set<my_class, my_class::compare_instances>::const_iterator found_it
                  = my_class_set.find(18);
 std::fprintf(stderr, "found_it->an_int = %d\n", found_it->an_int); 

Output: "found_it->an_int = 18" !!!!!!

I would have expected the above code to not compile, and for the compiler to yell at me that " 18 is not of type my_class ". But it doesn't...

Shouldn't arguments to .find be of same type as the elements of the set itself? That's what the documentation seems to say...

cmo
  • 3,762
  • 4
  • 36
  • 64
  • look at your constructor: `my_class(int new_int)`. you can create a `my_class` starting from an `int`. try and `find("blah")` and it won't compile :) – vulkanino Feb 28 '12 at 15:05

1 Answers1

12

This works because int is implicitly convertible to your class. Any constructor that is not marked explicit and can be called with exactly one argument that is not of the type of the class itself defines an implicit conversion. Effectively this means that, whenever an object of class-type is expected, you can also use an int, and it will automatically be converted.

Björn Pollex
  • 75,346
  • 28
  • 201
  • 283
  • Ok, you've added all my points to your answer. I'm removing my other comments. 8v) – Fred Larson Feb 28 '12 at 15:07
  • @FredLarson even if maybe unrelated, would you recommend to **always** declare explicit costructors? – vulkanino Feb 28 '12 at 15:08
  • @vulkanino: If you don't want implicit conversion, it might not be a bad idea. I'm not aware of any issue with adding `explicit` to a constructor even if it can't be used for implicit conversion. – Fred Larson Feb 28 '12 at 15:09
  • wowowow, nice, what a subtle detail. Do you think this is good practice? It certainly makes things easy - I can have a `set` of Objects an search via an integer (thanks to the implicit constructor). But of course the implicit constructor builds the entire Object to do the search... which is wasteful. Is there a better way to do this? To search for my objects in a set? I can use a `map` where the key is the would-be-member-variable-for-comparator... but that adds a level of complexity. – cmo Feb 28 '12 at 15:10
  • @CycoMatto: [This question](http://stackoverflow.com/questions/2202731/is-there-support-in-c-stl-for-sorting-objects-by-attribute) deals with that problem. – Björn Pollex Feb 28 '12 at 15:25