1

I noticed something today. If I create three versions of an overloaded + operators to handle every combination ( object + primitive, primitive + object, object + object) everything executes as expected:

class Int
{ int data;
  public:  
    Int (){data = 0; };
    Int (int size){ data = size; };
    friend int operator+(Int, Int);
    friend int operator+(int, Int);
    friend int operator+(Int, int);
};
int operator+(Int lInt, Int rInt) 
{   cout <<"first version. "; 
    return rInt.data + lInt.data; 
}
int operator+(int lInt, Int rInt) 
{   cout <<"second version. "; 
    return rInt.data + lInt; 
}
int operator+(Int lInt, int rInt)
{   cout <<"third version. ";
    return lInt.data + rInt;
}
int main(int argc, char *argv[]) {

    Int int1 = 1;

    cout <<  int1 + int1 <<endl; // prints "first version. 2"
    cout <<  3 + int1 << endl;   // prints "second version. 4"
    cout <<  int1 + 3 << endl;   // prints "third version. 4"
}

But if I delete the second and third versions it still works!?!

class Int
{ int data;
  public:  
    Int (){data = 0; };
    Int (int size){ data = size; };
    friend int operator+(Int, Int);
};
int operator+(Int lInt, Int rInt) 
{   cout <<"first version. "; 
    return rInt.data + lInt.data; 
}
int main(int argc, char *argv[]) {

    Int int1 = 1;

    cout <<  int1 + int1 <<endl; // prints "first version. 2"
    cout <<  3 + int1 << endl;   // prints "first version. 4"
    cout <<  int1 + 3 << endl;   // prints "first version. 4"
}

How is my overloaded + operator, which is meant to accept two objects, able to take an int and object. How is it able to take object and an int? I hope I'm not overlooking something stupidly obvious here!

Jeff-Russ
  • 341
  • 2
  • 10

2 Answers2

0

The culprit is this guy:

Int (int size){ data = size; };

Since it is not marked explicit, that is an implicit constructor, and the compiler will automatically shove a call to it in contexts where you have an int but a function (or operator) is expecting an Int.

So, for example, the compiler resolves

cout <<  3 + int1 << endl;

to

cout <<  Int(3) + int1 << endl;

automatically.

It is often a good idea to use the explicit keyword to force this kind of code to be more explicit, however, it should be mentioned that implicit construction is heavily used in very prominent code, most notably the C++ Standard Library, where, for example, a std::string can be implicitly constructed from a const char*. It's a tradeoff between convenience and clarity.

Community
  • 1
  • 1
bgoldst
  • 34,190
  • 6
  • 38
  • 64
0

You've defined an implicit conversion from int to Int:

Int (int size){ data = size; };

So the compiler can figure out that Int + int should call Int + Int(int).

If you didn't want that, for whatever reason, you would mark the constructor as explicit.

rici
  • 234,347
  • 28
  • 237
  • 341
  • AHH! I see! But if a constructor has no return value how is std::cout getting the answer? – Jeff-Russ Apr 04 '15 at 20:11
  • @Jeff-Russ: The "return value" of a constructor is the constructed object. And the argument to `<<` is the result of `operator+(Int, Int)`, whose value is an `int`, not an `Int`. – rici Apr 04 '15 at 20:12