2

I have this code:

#include <iostream>
using namespace std;

class complex
{
    double re;
    double im;

public:
    complex(): re(0), im(0) {}
    complex(double x) {re = x, im = x;}
    complex(double x, double y) {re=x, im =y;}
    void print() {cout << re << " " << im;}
};

int main()
{
    complex c1;
    double i=2;
    c1 = i;
    c1.print();

    return 0;
}

My question is, why the code in this line compiles.

c1 = i;

The compiler gives no error(or warning), why?

Karen Baghdasaryan
  • 2,407
  • 6
  • 24
Joc380
  • 25
  • 7
  • 1
    dont `using namespace std;` and then use names already present in the std namespace. Thats a recipe for confusing code – 463035818_is_not_an_ai Sep 01 '21 at 12:11
  • Did you try setting breakpoints in the various constructors for `complex`? That would give you an idea of what is happening (and you could read a good book to learn more). – crashmstr Sep 01 '21 at 12:12
  • 3
    Unrelated, but most people would expect `complex(r)` to result in a complex number with no imaginary part; not r + ri, but r + 0i. – molbdnilo Sep 01 '21 at 12:29
  • 1
    @molbdnilo well, most people who know about complex numbers. I expect *most people* would look at you with a confused expression. – user253751 Sep 01 '21 at 12:35
  • 2
    @user253751 All the more because they don't speak much English. Context, I say, context. – Peter - Reinstate Monica Sep 01 '21 at 12:37

4 Answers4

5

Let's examine what happens in the line.

c1 = i;

What happens is that operator= is called for class complex, which, in this case, is implicitly defined.

/////////////////////////////////////
//implicit copy assignment operator//
/////////////////////////////////////

complex& operator=(const complex& cmp)
{
    //the default implicit function of this operator is copying every member of cmp into this.
}

So it takes const complex& as argument, which can be bound to rvalue of type complex, then the compiler searches to see if there is a constructor accepting double parameter, so the expression is resolved into.

c1 = complex(i);

Which, obviously, can be executed.

Karen Baghdasaryan
  • 2,407
  • 6
  • 24
3

c1 = i invokes the constructor complex(double x) {re = x, im = x;}. If you'd prefer it didn't you can specify the constructor as explicit, like so:

explicit complex(double x) {re = x, im = x;}

Then the compiler would issue an error @ c1 = i;

KeyC0de
  • 4,728
  • 8
  • 44
  • 68
1

Because your constructor isn't explicit.

A constructor with a single non-default parameter (until C++11) that is declared without the function specifier explicit is called a converting constructor.

So having a constructor with a single non-default parameter, such as your complex(double x) will enable you to write complex s = 5.0 and call said constructor.

adnan_e
  • 1,764
  • 2
  • 16
  • 25
1

I have added some comments that explain how this program works. When you write c1 = i; then a temporary of type complex will be created using the converting constructor and then that temporary will be assigned to c1 using the assignment operator.

#include <iostream>

using namespace std;

class complex
{
   double re;
   double im;

   public:
       complex(): re(0), im(0){}
       //this converting constructor will be used to create a temporary of type complex from variable i
       complex(double x) 
       {
           std::cout<<"single parameter constructor used"<<std::endl;
           re = x;
           im = x;
           
       }
       complex(double x, double y) {re=x, im =y;}
       void print() {cout << re << " " << im;}
      
      //this assignment operator = will be used to assign the temporary(of type complex) created above to variable c1  
       complex& operator=(const complex &rhs)
       {
           std::cout<<"assignment operator used"<<std::endl;
       }
       complex(const complex&)
      {
          std::cout<<"copy constructor used"<<std::endl;
      }
};

int main()
{
    complex c1;
    double i=2;
    c1 = i;//first a temporary of type complex will be created using the converting constructor and then that temporary will be assigned to c1 using the assignment operator=
    c1.print();

    return 0;
}

If you want to prevent this kind of usage, you can make the converting constructor explicit by adding the keyword explicit in front of the converting constructor.

Jason
  • 36,170
  • 5
  • 26
  • 60