14

What is the difference between this:

TestClass t;

And this:

TestClass t = TestClass();

I expected that the second might call the constructor twice and then operator=, but instead it calls the constructor exactly once, just like the first.

Andrew
  • 1,343
  • 12
  • 14

4 Answers4

16
TestClass t;

calls the default constructor.

TestClass t = TestClass();

is a copy initialization. It will call the default constructor for TestClass() and then the copy constructor (theoretically, copying is subject to copy elision). No assignment takes place here.

There's also the notion of direct initialization:

TestClass t(TestClass());

If you want to use the assignment operator:

TestClass t;
TestClass s;
t = s;
Luchian Grigore
  • 253,575
  • 64
  • 457
  • 625
  • I'm wondering, could the second also call the move constructor in C++11? –  Aug 07 '12 at 20:39
  • 8
    The call to the copy-constructor might be optimized away (and usually is). – Björn Pollex Aug 07 '12 at 20:39
  • @Mahesh copying is subject to elision. – Luchian Grigore Aug 07 '12 at 20:42
  • 2
    @Mahesh - that's just RVO in action – Flexo Aug 07 '12 at 20:42
  • Ah, copy elision and mistaking initialization for assignment is what tripped me up. The anonymous TestClass() is an rvalue. Because it has no name and is destined for destruction anyway the compiler just skips the copy and uses it directly. The behavior I expected can be had from `TestClass t; t = TestClass();` which does indeed do two constructions and an assignment. – Andrew Aug 07 '12 at 20:56
  • With `typedef int TestClass;`, the first two code examples are very different. – Kerrek SB Aug 07 '12 at 21:12
  • Is there a way to get the exact behavior of `TestClass t;` in templated code, with the exception that, if `TestClass` is a primitive, then `t` is zero-initialized instead of remaining uninitialized? – user541686 Aug 07 '12 at 21:16
  • @Mehrdad I don't understand the question. – Luchian Grigore Aug 07 '12 at 21:17
  • @LuchianGrigore: Let's motivate it. Here is the problem: I want `t` to be *always* initialized to the default value of the type (but to never remain uninitialized). If `TestClass` is POD, then `t` should be initialized to the value of `TestClass()`; if not, then *only* the default constructor for `t` should be called, and nothing else. Is there a way to do that? – user541686 Aug 07 '12 at 21:19
  • @Mehrdad if t has a default constructor, it will be called. If it's a POD, you'll have to do `TestClass t = TestClass()` to value-initialize it. – Luchian Grigore Aug 07 '12 at 21:21
  • @LuchianGrigore: Yes, that's what I already mentioned. The question is, if I'm inside a template, then I have no idea what `TestClass` is, so I don't know what to write. Is there any way to get that initialization behavior then, without having to duplicate my code? – user541686 Aug 07 '12 at 21:22
  • @Mehrdad `std::is_pod` might help in this case. – Luchian Grigore Aug 07 '12 at 21:23
  • @LuchianGrigore: That requires duplicating the rest of my code or taking the rest into a whole new function though... which is quite painful, hard to read, and, in practice, not worth it. Also, it's C++11-only. (If the answer is "there isn't any way" then that's a valid answer too, that's why I'm asking.) – user541686 Aug 07 '12 at 21:24
  • @Mehrdad I'm cautions in saying there's no other way. There might be. Ask a question... – Luchian Grigore Aug 07 '12 at 21:28
4

The first case is quite simple - constructs an instance using the default constructor.

The second class is Constructing an anonymous object and then calling the copy constructor. Notice that here the = is not assignment, it's similar to (but not identical) writing:

TestClass t(TestClass());

We can verify that this needs the copy constructor to be available by making it unavailable, e.g.:

#include <iostream>

struct TestClass {
  TestClass() { std::cout << "Ctor" << std::endl; }
  TestClass(const TestClass&)  = delete;
};

int main() {
  TestClass t = TestClass();
}

Which fails to compile because of the deleted copy constructor. (In C++03 you can use private: instead).

What's actually happening most likely though is that your compiler is doing Return value optimisation, whereby it's allowed to ommit the call to the copy constructor entirely provided a suitable one exists and would be accessible.

Community
  • 1
  • 1
Flexo
  • 87,323
  • 22
  • 191
  • 272
  • Actually, copy and direct initialization are not the same. http://stackoverflow.com/questions/11222076/why-is-copy-constructor-called-instead-of-conversion-constructor – Luchian Grigore Aug 07 '12 at 20:42
2

In the first one, you are calling the default constructor implicitly. And in the second one you're calling it explicitly.

Kidus
  • 1,785
  • 2
  • 20
  • 36
  • Would `TestClass t(0)` and `TestClass t = TestClass(0)` be any different? – Andrew Aug 07 '12 at 20:48
  • 1
    well, it's not different. But it's up to the compiler to decide. It will either do it like the first declaration or it would create a temporary object that is later on copied to "t". If it does it that way (by creating a temp object) the class destructor will be called after the temporary object created is removed. But one way or another you'll get the same result. – Kidus Aug 07 '12 at 20:57
1

The latter one could call copy constructor and thus requires one to be public.

Edit: I certainly drew far too big conclusions from the type name you used. The sentence above only applies for class-types (i.e. not POD). For POD types, the former leaves the variable uninitialized, while the latter initializes it with so-called "default" value.

eq-
  • 9,986
  • 36
  • 38