1

I am trying to understand conversion constructors. I am using the following piece of code

class cls 
{
public:
  cls()
  {
      std::cout << "Regular constructor \n";     ---> Line A
  }
  cls (int a) //Constructing converter
  {
      std::cout << "Int constructor \n";         ---> Line B
  }

  cls (cls& d) //Copy constructor
  {
      std::cout << "Copy constructor \n";        ---> Line C
  }
};


int main()
{
    cls d;
    std::cout << "-----------------------\n";
    cls e = 15; //int constructor then copy constructor
        return;
}

Now I am confused at the statement cls e = 15 my understanding was that this statement was suppose to call Line B(Conversion Cont) and then Line C (Copy constructor) however it only called Line B. I though cls e = 15 was equivalent to cls e = cls(15). So I tried cls e = cls(15) which also only gives the Line B. I would appreciate it if someone could explain what happens when we use the following

cls e = cls(15) //I was expecting a conversion constructor followed by copy constructor but apparently i was wrong. Any explanation on what is happening would be appreciated

Rajeshwar
  • 11,179
  • 26
  • 86
  • 158
  • sorry let me correct it – Rajeshwar Aug 25 '13 at 13:56
  • The compiler is free to elide unnecessary copies. You're right, in practice the copy constructor would get called. – Luchian Grigore Aug 25 '13 at 13:57
  • 1
    Remarks: `cls (cls& d)` should probably be `cls (const cls& d)` (or `cls (cls const& d)`), and `return;` should certainly be `return 0;`. – gx_ Aug 25 '13 at 14:01
  • Is the code really as presented? In particular, does the copy constructor really take a non-const reference? If so, then the code should not compile in the first place! – David Rodríguez - dribeas Aug 25 '13 at 14:04
  • Yes the code does compile VS2010 – Rajeshwar Aug 25 '13 at 14:05
  • @Rajeshwar It only compiles because of an "evil" compiler extension (and should give you a warning with level `/W4` or `/Wall`). With G++ (after fixing the `return 0;`, commenting line comments and adding `#include `): http://ideone.com/79IAs0 (vs http://ideone.com/cpAB6G ) – gx_ Aug 25 '13 at 14:15

3 Answers3

3

This is due to copy elision compiler optimization. An compiler is allowed to elide copy constructor calls in certain cases. What you see is this optimization in action. You are right in assuming calls to:

  • Conversion constructor and then a
  • Copy constructor

But in this case the second call is elided/removed/optimized by the constructor by using return value optimization. The compiler constructs the object directly in to e rather than creating a temporary object and then copying it to e.

If you are using GCC you can use -fno-elide-constructors option to disable copy-elision and you should see the result you expected.

Alok Save
  • 202,538
  • 53
  • 430
  • 533
  • This would be correct, except that the code is ill-formed and should not compile. While the compiler is allowed to elide the copy, it must verify the availability of the copy constructor, even if it is elided. In this case, the copy constructor has the peculiarity that it takes the argument by non-const reference, which makes the program incorrect. – David Rodríguez - dribeas Aug 25 '13 at 14:06
  • @DavidRodríguez-dribeas: True, The copy constructor should be available and accessible as well. Probably OP missed the `const` on the copy ctor argument while copy pasting the code. – Alok Save Aug 25 '13 at 14:12
  • Quick question so does this mean that the statement `cls a; cls c =a` mean `cls c = cls(a)` so actually first the copy constructor of cls(a) is called then another copy constructor for `cls c` is called ? – Rajeshwar Aug 25 '13 at 14:38
  • @Rajeshwar: Yes. But as I said above copy elision can elide the excessive copy constructor calls which generate temporary useless copies. – Alok Save Aug 25 '13 at 14:55
  • Any idea how we can temporarily disable it in VS2010 ? – Rajeshwar Aug 25 '13 at 14:57
  • @Rajeshwar: AFAIK, there is no way to do so in VS. – Alok Save Aug 25 '13 at 14:59
  • @Rajeshwar @AlokSave "does this mean that the statement `cls a; cls c =a` mean `cls c = cls(a)`": _No_, because here `a` and `c` have _the same type_ (which wasn't the case for `cls e = 15;`), so `cls c = a;` is the same as `cls c(a);` (except if the copy constructor were `explicit`, in which case `cls c = a;` wouldn't compile). So, with `-fno-elide-constructors` to disable copy elision, `cls c = cls(a);` would call the copy constructor twice, but `cls c = a;` will always call it only once. (I have verified it.) – gx_ Aug 25 '13 at 15:28
  • @Gx_ I agree I just tried this on http://coliru.stacked-crooked.com/ with `-fno-elide-constructors` and this only happens when they are of different types – Rajeshwar Aug 25 '13 at 15:30
0

It seems that the compiler is optimising your code not ot call the copy constructor which is known as copy elision

Please look HERE for more details.

Basically in copy elision a compiler optimizez your code to omit unnessesary intermediate temporary objects.

Saksham
  • 9,037
  • 7
  • 45
  • 73
0

You are right about that cls e = 15 is the same as cls e = cls(15). This is because your cls(int a) is not declared explicit. Next is just compiler copy elision optimization.

Nazar554
  • 4,105
  • 3
  • 27
  • 38
  • `This is because your cls(int a) is not declared explicit` - it has nothing to do with it being declared `explicit` or not. – Luchian Grigore Aug 25 '13 at 14:01
  • 1
    I think if we had `explicit cls(int a)` then `cls t = 15` would not compile. `error: conversion from ‘int’ to non-scalar type ‘cls’ requested` – Nazar554 Aug 25 '13 at 14:04
  • 2
    @LuchianGrigore: It has very much to do with it. If the constructor is declared explicit, then this will compile: `cls e(15);` -- But this will not: `cls e = 15;` – Benjamin Lindley Aug 25 '13 at 14:04
  • @BenjaminLindley I know, but that's not the reason the copy constructor isn't called. – Luchian Grigore Aug 25 '13 at 15:30