4

Consider simple statement (Taken from Is there a difference in C++ between copy initialization and direct initialization?):

A c2 = A();

This statement value-initializes a temporary and then copies that value into c2 (Read 5.2.3/2 and 8.5/14). This of course will require a non-explicit copy constructor (Read 8.5/14 and 12.3.1/3 and 13.3.1.3/1)

[Mind the bold sentence in above para] -> My question is why?

Now consider this code :

class B {};
struct A 
{
  A(B const&) {}
  A(A const&) = delete;
  //A(A const&); //delete above statement and uncomment this statement, 
  //and everything works, even though there in no body of copy constructor Oo
};

A a2 = B();    //error since there is no copy constructor oO

Why copy-initialization requires presence of copy constructor even though it's not needed sometime as presented in above code

Please please one more thing :

While direct initialization has all constructors available to call, and in addition can do any implicit conversion it needs to match up argument types, copy initialization can just set up one implicit conversion sequence.

[Mind the bolding in the following para]

Doesn't that means direct initialization have access to all constructors and can perform implicit conversion sequence , while copy initialization all can do is perform implicit conversion sequence? . What I mean to ask is , implicit conversion in direct initialization is different from implicit conversion sequence in copy initialization ?

Community
  • 1
  • 1
Angelus Mortis
  • 1,534
  • 11
  • 25
  • 2
    Since C++11 this is a copy/move context, so move constructor will be preferred to copy constructor. (This doesn't really affect your question) – M.M Mar 12 '16 at 22:21
  • 2
    In `A c2 = A();`, you say "why is a copy constructor needed?" Please explain how you think the `A()` object's value is transferred into the object `c2` , if not by a copy constructor. – M.M Mar 12 '16 at 22:24
  • For your second question read [this answer](http://stackoverflow.com/a/12953129/1505939). The version with `A(A const&);` is ill-formed with no diagnostic required because it violates the One Definition Rule (there must be one body for each function that is *odr-used*, however your program has none) – M.M Mar 12 '16 at 22:29
  • @M.M Thanks a lot sir for your helping comments, can you please also answer my last question with line "Please please one more thing : " ? . If possible can you please turn your comments into an answer? Thanks – Angelus Mortis Mar 13 '16 at 08:47
  • The last one is really a separate question, you should have posted two different ones. ALso I don't understand the question – M.M Mar 13 '16 at 09:24

2 Answers2

0

The rules for evaluating

A a1 = B();   // (1) copy-initialization
A a2 = {B()}; // (2) copy-initialization
A a3{B()};    // (3) direct-initialization

come from [dcl.init]/17:

— If the initializer is a (non-parenthesized) braced-init-list, the object or reference is list-initialized (8.5.4).
— [...]
— If the destination type is a (possibly cv-qualified) class type:

  • If the initialization is direct-initialization, or if it is copy-initialization where the cv-unqualified version of the source type is the same class as, or a derived class of, the class of the destination, constructors are considered. [...]
  • Otherwise (i.e., for the remaining copy-initialization cases), user-defined conversion sequences that can convert from the source type to the destination type or (when a conversion function is used) to a derived class thereof are enumerated as described in 13.3.1.4, and the best one is chosen through overload resolution (13.3). [...] The result of the call (which is the temporary for the constructor case) is then used to direct-initialize, according to the rules above, the object that is the destination of the copy-initialization. In certain cases, an implementation is permitted to eliminate the copying inherent in this direct-initialization by constructing the intermediate result directly into the object being initialized; see 12.2, 12.8.

For both a2 and a3, the initializer is a braced-init-list, so we just do list-initialization. This ends up calling the B const& constructor.

For a1, the first sub-bullet doesn't apply - since the source type (B) is not the same or derived class of the destination type (A). So we go into the second sub-bullet point which involves considering conversion functions. There is one (A(B const&)) so we effectively rewrite the expression

A a1_new{A{B{}}};

Now typically, this extra copy will be elided. But you're explicitly prohibiting it, so the code cannot compile.


As to why the differentiation? I don't know. It seems like copy-initialization should simply be syntactic sugar for direct-initialization. In most cases, after all, it is...

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 1
    `A a1_new(A(B()));` is most vexing parse – Piotr Skotnicki Mar 12 '16 at 20:20
  • @PiotrSkotnicki Blah, just turned everything into braces. – Barry Mar 12 '16 at 20:24
  • "*It seems like copy-initialization should simply be syntactic sugar for direct-initialization. In most cases, after all, it is...*" The prvalue changes in C++17 ensure it always is, no? – ildjarn Jul 21 '18 at 04:57
  • @ildjarn No. I should probably delete that rant paragraph anyway. It's not like we want `std::vector v = 3;` to compile... – Barry Jul 21 '18 at 06:08
0

(The following applies to C++11)To avoid rambling too much, A a2 = B(); has two stage : first, A temp object of type B is created when you write B(). Then, your function A(B const&) {} will not be invoked here with the role of directly initializing A because you use the copy-initialization syntax. If you want to invoke it to directly initialize A, you should write A a2(B()) instead, rather than using the copy-initialization syntax(the devil here is =). So, what's next? You get a temp object of type B, and you want to use it to initialize obj of type A, and now, you indirectly initialize A by converting B() to the type A. So your function A(B const&) {} is used as a type conversion rather than directly initializing A.

Because of conversion, a temp obj of type A is created, and then we need copy constructor to initialize a2 using that temporary obj.

In copy initialization, type-conversion-functions with explicit keyword can't be called. The design philosophy of explicit keyword is that you should directly invoke it. Combined with type-conversion-functions, those explicit attributed functions should not be used for implicit type conversion. In direct initialization, they can all be called even they are explicit.

To make things interesting, if you make your copy constructor and move constructor explicit, you can't even write T obj = T{}, but you can write T obj {T{}}. You can view copy and move ctor as a conversion too, only the destination and source type are of the same class.

I would like to provide you with further readings here. After that, read this question to know about copy-list-initialization and this question to learn about aggregate-types.

Han XIAO
  • 1,148
  • 9
  • 20