5

What the difference between A a1(5); and A a2 = A(5) ? Both of the works, but I really want know the difference between them, because I used method 2 in one of my project and I suffered from a bug which is fixed after I change to method 1. Thanks in advance!

class A {
public:
    int val;
    A() : val(0) {}
    A(int newVal) : val(newVal) {}
};

int main()
{
    A a1(5);  // method 1
    A a2 = A(5); // method 2
}
Bon
  • 3,073
  • 5
  • 21
  • 40

2 Answers2

8
A a1(5);  // method 1
A a2 = A(5); // method 2

The first one is called direct initialization, the second one is called copy-initialization.

The second one will NOT compile if you make the copy-constructor inaccessible or/and don't define it as:

class A {
public:
    int val;
    A() : val(0) {}
    A(int newVal) : val(newVal) {}
private:
    A(A const&);  //the second one will not compile
};

Now the second one will not compile. Note that it will not compile in both cases:

  • If the copy-constructor is defined, but is inaccessible (either it is private or protected).
  • If the copy-constructor is declared, but not defined.
Nawaz
  • 353,942
  • 115
  • 666
  • 851
  • Why would the second one fail to compile? Its not `A a2 = 5`. – K-ballo Jun 22 '12 at 04:27
  • @K-ballo: Oops. I misunderstood it. I wanted to say copy-constructor. – Nawaz Jun 22 '12 at 04:27
  • That might've been my comment. I missed the `A(...)` part. – chris Jun 22 '12 at 04:27
  • Thanks Nawaz, so what if the default constructor A() : val(0) {} is not there, what exactly will happen when I call A a2 = A(5); ? – Bon Jun 22 '12 at 04:31
  • @XuFeng: It doesn't matter. The default constructor has no role in your code as of now. – Nawaz Jun 22 '12 at 04:32
  • 1
    Note that according to the standard the code using copy initialization *shouldn't* compile if the copy constructor isn't available -- but nearly every compiler on earth will elide the copy, and most also ignore the requirement for a copy constructor to be accessible, except (possibly) in their most pedantic mode. – Jerry Coffin Jun 22 '12 at 04:37
  • @JerryCoffin: I think this part is incorrect : *but nearly every compiler on earth will elide the copy, and most also ignore the requirement for a copy constructor to be accessible, except (possibly) in their most pedantic mode*... because the copy-constructor is needed for semantic check even when in actuality it is not called due to copy-elision. – Nawaz Jun 22 '12 at 05:09
  • @Nawaz: I don't know of a single one that will reject the code with default flags. Even Comeau requires "strict errors" mode to reject it. – Jerry Coffin Jun 22 '12 at 05:26
  • @JerryCoffin: Here you go : http://ideone.com/ZvKAt .. semantic-check is performed , even when copy-constructor is not called : http://ideone.com/di1eM – Nawaz Jun 22 '12 at 05:29
  • @JerryCoffin: I think, the semantic check makes sense, because the copy-elision (like other optimizations) is done in the later phases. – Nawaz Jun 22 '12 at 05:33
  • Thanks, Nawaz, but if I add a public copy constructor as follows A(const A &) {cout << "copy constructor invoked " << endl;} The "copy constructor invoked " message is not printed. Is there any reason for this? – Bon Jun 22 '12 at 05:41
  • @Nawaz: I probably should have checked with newer compilers before I said that -- current versions of both gcc and VC++ reject the code by default. – Jerry Coffin Jun 22 '12 at 05:59
  • 1
    @XuFeng: It is due to copy-elision (an optimization). Try making the copy-constructor `private`, your code will not compile. So the copy-constructor is needed for semantic check even when it is not being called eventually. – Nawaz Jun 22 '12 at 05:59
1

Strictly speaking, reading your question I expected you to provide these two examples

A a1(5);  // method 1
A a2 = 5; // method 2

The first one is direct-initialization. The second one is copy-initialization.

The examples you provided in your question actually already illustrate the difference between the two :)

Direct-initialization initializes the target object directly, as a single-step process by finding and using an appropriate constructor. Copy-initialization is conceptually a two-step process: first it constructs a temporary object of type A by some conversion constructor and then it copies it to the destination object by using a copy-constructor. I.e.

A a2 = 5;

will actually work as

A a2 = A(5);

And that actually exhaustively explains the difference.

The two-step structure of the second variant is, again, conceptual. The compilers are allowed (and will) optimize the second variant so that it will work exactly as the first one. They will eliminate the intermediate temporary object and perform initialzation directly.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • 1
    I think `A a2 = 5;` is not equivalent to `A a2 = A(5);`. Think of `explicit` constructor. – Nawaz Jun 22 '12 at 04:34
  • @Nawaz: Good point. I implied one-directional relationship, but said "equivalent", which is a bad choice of word. I replaced "equivalent" with "works as". – AnT stands with Russia Jun 22 '12 at 04:36
  • +1 for the detailed explanation. I wish you also explain the accessible copy-constructor in case of copy-elision. – Nawaz Jun 22 '12 at 05:15