Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

What is the point of calling constructor with implicit conversion instead of assignment operator after an object is initialized?

Tags:

c++

c++11

Consider following example:

#include <iostream>
using std::cout;
using std::endl;

class CBox
{
public:
    CBox(double lv = 1.0, double wv = 1.0, double hv = 1.0) :
        m_Length {lv}, m_Width {wv}, m_Height {hv}
    {
        cout << "Constructor called" << endl;   
    }

    //CBox& operator=(double)
    //{
    //  cout << "Assignment operator called" << endl;
    //  return *this;
    //}

    double volume() const { return m_Length* m_Width* m_Height; }

private:
    double m_Length;
    double m_Width;
    double m_Height;
};

int main()
{
    CBox box {1.0, 1.0, 1.0}; // no need for initializer list, but put it anyway
    box = 2.0; // why is this calling constructor again ?!
    cout << box.volume() << endl; // prints 2
}

Note, that I intentionally commented out the overloaded assignment operator.

Executing this program results in following output:

Constructor called
Constructor called
2

I noticed, that even if box object has been initialized, the next statement is deliberately calling the constructor again. What is the point of that?

I know that this can prevented by the explicit keyword, so the constructor would be:

explicit CBox(double lv = 1.0, double wv = 1.0, double hv = 1.0) :
        m_Length {lv}, m_Width {wv}, m_Height {hv}
    {
        cout << "Constructor called" << endl;   
    }

but I would rather expect it to affect constructs like:

CBox box = 2.0;

Of course, when I uncomment overloaded assignment operator, then it takes precedence over constructor.

like image 604
Grzegorz Szpetkowski Avatar asked May 22 '16 10:05

Grzegorz Szpetkowski


2 Answers

There is no implicit CBox::operator=(double), so box = 2.0; has to create a temporary CBox object. It's equivalent to box = CBox(2.0);.

Making your constructor explicit disallows the implicit conversion from double to CBox, so no appropriate assignment operator exists, and you get a compile error.

like image 156
Miles Budnek Avatar answered Oct 12 '22 18:10

Miles Budnek


There is a great difference between

CBox box = 2.0

and

box = 2.0

The first is equivalent to

CBox box = CBox(2.0)

While the second is equivalent to

box.operator=(2.0)

Since your constructor's arguments have default values, this is equivalent to defining 3 different constructors:

CBox(double, double, double)
CBox(double, double) // the third arguments gets default value
CBox(double) // second and third arguments gets default values

So, calling the constructor explicitly with a single double will invoke your constructor and will give the second and third arguments default values. And now for the real mystery: why did calling the operator= eventually result in calling a constructor?

Since you don't have an explicitly defined operator=, the compiler will auto-generate an assignment operator that takes another CBox as its single parameter; this will make a copy of the argument.

When you try to assign anything into your CBox object the compiler will attempt to call operator=(const CBox&); since you provide a double and not a CBox, the compiler will try to convert the double into a CBox in one of many possible ways:

  1. through non-explicit constructor (the third one in your case)
  2. through conversion operator (not relevant in this case)
  3. probably some other ways I'm not familiar with.

So, eventually this code:

box = 2.0

will be translated to:

box.operator=(Cbox(2.0))
like image 41
Uri Brecher Avatar answered Oct 12 '22 20:10

Uri Brecher