Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <h2>C++17 Update</h2> <p>In C++17, the meaning of <code>A_factory_func()</code> changed from creating a temporary object (C++&lt;=14) to just specifying the initialization of whatever object this expression is initialized to (loosely speaking) in C++17. These objects are the variables created by a declaration (like <code>a1</code>) or artificial objects created when the initialization ends up being discarded otherwise or if an object is needed for reference binding (like, in <code>A_factory_func();</code>, an object is artificially created, called "temporary materialization", because <code>A_factory_func()</code> doesn't have a variable or reference that otherwise would require an object to exist). Also, these objects are called "result objects".</p> <p>As examples in our case, in the case of <code>a1</code> and <code>a2</code> special rules say that in such declarations, the result object of a prvalue initializer of the same type as <code>a1</code> is variable <code>a1</code>, and therefore <code>A_factory_func()</code> directly initializes the object <code>a1</code>. Any intermediary functional-style cast would not have any effect, because <code>A_factory_func(another-prvalue)</code> just "passes through" the result object of the outer prvalue to be also the result object of the inner prvalue.</p> <hr> <pre><code>A a1 = A_factory_func(); A a2(A_factory_func()); </code></pre> <p>Depends on what type <code>A_factory_func()</code> returns. I assume it returns an <code>A</code> - then it's doing the same - except that when the copy constructor is explicit, then the first one will fail. Read <a href="http://eel.is/c++draft/dcl.init#14" rel="noreferrer">8.6/14</a></p> <pre><code>double b1 = 0.5; double b2(0.5); </code></pre> <p>This is doing the same because it's a built-in type (this means not a class type here). Read <a href="http://eel.is/c++draft/dcl.init#14" rel="noreferrer">8.6/14</a>. </p> <pre><code>A c1; A c2 = A(); A c3(A()); </code></pre> <p>This is not doing the same. The first default-initializes if <code>A</code> is a non-POD, and doesn't do any initialization for a POD (Read <a href="http://eel.is/c++draft/dcl.init#9" rel="noreferrer">8.6/9</a>). The second copy initializes: Value-initializes a temporary and then copies that value into <code>c2</code> (Read <a href="http://eel.is/c++draft/expr.type.conv#2" rel="noreferrer">5.2.3/2</a> and <a href="http://eel.is/c++draft/dcl.init#14" rel="noreferrer">8.6/14</a>). This of course will require a non-explicit copy constructor (Read <a href="http://eel.is/c++draft/dcl.init#14" rel="noreferrer">8.6/14</a> and <a href="http://eel.is/c++draft/class.conv.ctor#3" rel="noreferrer">12.3.1/3</a> and <a href="http://eel.is/c++draft/over.match.ctor#1" rel="noreferrer">13.3.1.3/1</a> ). The third creates a function declaration for a function <code>c3</code> that returns an <code>A</code> and that takes a function pointer to a function returning a <code>A</code> (Read <a href="http://eel.is/c++draft/dcl.ambig.res" rel="noreferrer">8.2</a>).</p> <hr> <p><strong>Delving into Initializations</strong> Direct and Copy initialization</p> <p>While they look identical and are supposed to do the same, these two forms are remarkably different in certain cases. The two forms of initialization are direct and copy initialization:</p> <pre><code>T t(x); T t = x; </code></pre> <p>There is behavior we can attribute to each of them:</p> <ul> <li>Direct initialization behaves like a function call to an overloaded function: The functions, in this case, are the constructors of <code>T</code> (including <code>explicit</code> ones), and the argument is <code>x</code>. Overload resolution will find the best matching constructor, and when needed will do any implicit conversion required. </li> <li>Copy initialization constructs an implicit conversion sequence: It tries to convert <code>x</code> to an object of type <code>T</code>. (It then may copy over that object into the to-initialized object, so a copy constructor is needed too - but this is not important below)</li> </ul> <p>As you see, <em>copy initialization</em> is in some way a part of direct initialization with regard to possible implicit conversions: While direct initialization has all constructors available to call, and <em>in addition</em> can do any implicit conversion it needs to match up argument types, copy initialization can just set up one implicit conversion sequence. </p> <p>I tried hard and <a href="http://coliru.stacked-crooked.com/a/708ae8b380c63ba8" rel="noreferrer">got the following code to output different text for each of those forms</a>, without using the "obvious" through <code>explicit</code> constructors.</p> <pre><code>#include &lt;iostream&gt; struct B; struct A { operator B(); }; struct B { B() { } B(A const&amp;) { std::cout &lt;&lt; "&lt;direct&gt; "; } }; A::operator B() { std::cout &lt;&lt; "&lt;copy&gt; "; return B(); } int main() { A a; B b1(a); // 1) B b2 = a; // 2) } // output: &lt;direct&gt; &lt;copy&gt; </code></pre> <p>How does it work, and why does it output that result? </p> <ol> <li><p><strong>Direct initialization</strong></p> <p>It first doesn't know anything about conversion. It will just try to call a constructor. In this case, the following constructor is available and is an <em>exact match</em>: </p> <pre><code>B(A const&amp;) </code></pre> <p>There is no conversion, much less a user defined conversion, needed to call that constructor (note that no const qualification conversion happens here either). And so direct initialization will call it. </p></li> <li><p><strong>Copy initialization</strong></p> <p>As said above, copy initialization will construct a conversion sequence when <code>a</code> has not type <code>B</code> or derived from it (which is clearly the case here). So it will look for ways to do the conversion, and will find the following candidates</p> <pre><code>B(A const&amp;) operator B(A&amp;); </code></pre> <p>Notice how I rewrote the conversion function: The parameter type reflects the type of the <code>this</code> pointer, which in a non-const member function is to non-const. Now, we call these candidates with <code>x</code> as argument. The winner is the conversion function: Because if we have two candidate functions both accepting a reference to the same type, then the <em>less const</em> version wins (this is, by the way, also the mechanism that prefers non-const member function calls for non-const objects). </p> <p>Note that if we change the conversion function to be a const member function, then the conversion is ambiguous (because both have a parameter type of <code>A const&amp;</code> then): The Comeau compiler rejects it properly, but GCC accepts it in non-pedantic mode. Switching to <code>-pedantic</code> makes it output the proper ambiguity warning too, though. </p></li> </ol> <p>I hope this helps somewhat to make it clearer how these two forms differ!</p>
    singulars
    1. This table or related slice is empty.
    plurals
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. This table or related slice is empty.
    1. VO
      singulars
      1. This table or related slice is empty.
    2. VO
      singulars
      1. This table or related slice is empty.
    3. VO
      singulars
      1. This table or related slice is empty.
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload