Note that there are some explanatory texts on larger screens.

plurals
  1. PO
    primarykey
    data
    text
    <p>Here are the possible ways to take a unique pointer as an argument, as well as their associated meaning.</p> <h3>(A) By Value</h3> <pre><code>Base(std::unique_ptr&lt;Base&gt; n) : next(std::move(n)) {} </code></pre> <p>In order for the user to call this, they must do one of the following:</p> <pre><code>Base newBase(std::move(nextBase)); Base fromTemp(std::unique_ptr&lt;Base&gt;(new Base(...)); </code></pre> <p>To take a unique pointer by value means that you are <em>transferring</em> ownership of the pointer to the function/object/etc in question. After <code>newBase</code> is constructed, <code>nextBase</code> is guaranteed to be <em>empty</em>. You don't own the object, and you don't even have a pointer to it anymore. It's gone.</p> <p>This is ensured because we take the parameter by value. <code>std::move</code> doesn't actually <em>move</em> anything; it's just a fancy cast. <code>std::move(nextBase)</code> returns a <code>Base&amp;&amp;</code> that is an r-value reference to <code>nextBase</code>. That's all it does.</p> <p>Because <code>Base::Base(std::unique_ptr&lt;Base&gt; n)</code> takes its argument by value rather than by r-value reference, C++ will automatically construct a temporary for us. It creates a <code>std::unique_ptr&lt;Base&gt;</code> from the <code>Base&amp;&amp;</code> that we gave the function via <code>std::move(nextBase)</code>. It is the construction of this temporary that actually <em>moves</em> the value from <code>nextBase</code> into the function argument <code>n</code>.</p> <h3>(B) By non-const l-value reference</h3> <pre><code>Base(std::unique_ptr&lt;Base&gt; &amp;n) : next(std::move(n)) {} </code></pre> <p>This has to be called on an actual l-value (a named variable). It cannot be called with a temporary like this:</p> <pre><code>Base newBase(std::unique_ptr&lt;Base&gt;(new Base)); //Illegal in this case. </code></pre> <p>The meaning of this is the same as the meaning of any other use of non-const references: the function may <em>or may not</em> claim ownership of the pointer. Given this code:</p> <pre><code>Base newBase(nextBase); </code></pre> <p>There is no guarantee that <code>nextBase</code> is empty. It <em>may</em> be empty; it may not. It really depends on what <code>Base::Base(std::unique_ptr&lt;Base&gt; &amp;n)</code> wants to do. Because of that, it's not very evident just from the function signature what's going to happen; you have to read the implementation (or associated documentation).</p> <p>Because of that, I wouldn't suggest this as an interface.</p> <h3>(C) By const l-value reference</h3> <pre><code>Base(std::unique_ptr&lt;Base&gt; const &amp;n); </code></pre> <p>I don't show an implementation, because you <em>cannot</em> move from a <code>const&amp;</code>. By passing a <code>const&amp;</code>, you are saying that the function can access the <code>Base</code> via the pointer, but it cannot <em>store</em> it anywhere. It cannot claim ownership of it.</p> <p>This can be useful. Not necessarily for your specific case, but it's always good to be able to hand someone a pointer and know that they <em>cannot</em> (without breaking rules of C++, like no casting away <code>const</code>) claim ownership of it. They can't store it. They can pass it to others, but those others have to abide by the same rules.</p> <h3>(D) By r-value reference</h3> <pre><code>Base(std::unique_ptr&lt;Base&gt; &amp;&amp;n) : next(std::move(n)) {} </code></pre> <p>This is more or less identical to the "by non-const l-value reference" case. The differences are two things.</p> <ol> <li><p>You <em>can</em> pass a temporary:</p> <pre><code>Base newBase(std::unique_ptr&lt;Base&gt;(new Base)); //legal now.. </code></pre></li> <li><p>You <em>must</em> use <code>std::move</code> when passing non-temporary arguments.</p></li> </ol> <p>The latter is really the problem. If you see this line:</p> <pre><code>Base newBase(std::move(nextBase)); </code></pre> <p>You have a reasonable expectation that, after this line completes, <code>nextBase</code> should be empty. It should have been moved from. After all, you have that <code>std::move</code> sitting there, telling you that movement has occurred.</p> <p>The problem is that it hasn't. It is not <em>guaranteed</em> to have been moved from. It <em>may</em> have been moved from, but you will only know by looking at the source code. You cannot tell just from the function signature.</p> <h3>Recommendations</h3> <ul> <li><strong>(A) By Value:</strong> If you mean for a function to claim <em>ownership</em> of a <code>unique_ptr</code>, take it by value.</li> <li><strong>(C) By const l-value reference:</strong> If you mean for a function to simply use the <code>unique_ptr</code> for the duration of that function's execution, take it by <code>const&amp;</code>. Alternatively, pass a <code>&amp;</code> or <code>const&amp;</code> to the actual type pointed to, rather than using a <code>unique_ptr</code>.</li> <li><strong>(D) By r-value reference:</strong> If a function may or may not claim ownership (depending on internal code paths), then take it by <code>&amp;&amp;</code>. But I strongly advise against doing this whenever possible.</li> </ul> <h3>How to manipulate unique_ptr</h3> <p>You cannot copy a <code>unique_ptr</code>. You can only move it. The proper way to do this is with the <code>std::move</code> standard library function.</p> <p>If you take a <code>unique_ptr</code> by value, you can move from it freely. But movement doesn't actually happen because of <code>std::move</code>. Take the following statement:</p> <pre><code>std::unique_ptr&lt;Base&gt; newPtr(std::move(oldPtr)); </code></pre> <p>This is really two statements:</p> <pre><code>std::unique_ptr&lt;Base&gt; &amp;&amp;temporary = std::move(oldPtr); std::unique_ptr&lt;Base&gt; newPtr(temporary); </code></pre> <p>(note: The above code does not technically compile, since non-temporary r-value references are not actually r-values. It is here for demo purposes only).</p> <p>The <code>temporary</code> is just an r-value reference to <code>oldPtr</code>. It is in the <em>constructor</em> of <code>newPtr</code> where the movement happens. <code>unique_ptr</code>'s move constructor (a constructor that takes a <code>&amp;&amp;</code> to itself) is what does the actual movement.</p> <p>If you have a <code>unique_ptr</code> value and you want to store it somewhere, you <em>must</em> use <code>std::move</code> to do the storage.</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