<html>
<style>
ins {background-color:#A0FFA0}
del {background-color:#FFA0A0}
strong { color: #2020ff }
</style>
<title>Drafting for Core 1402</title>
<body>
Jason Merrill
<br>2012-04-16
<br>Revision 4
<br>N3667

<h1>Drafting for Core 1402</h1>

<h2>Introduction</h2>

This paper represents a small change to the drafting for core issue 1402
from the pre-meeting mailing, placed into a separate document so that it
can be moved at Bristol.

<h2>Issue text</h2>

<H4>1402.
  
Move functions too often deleted
</H4><B>Section: </B>12.8&#160; [class.copy]
 &#160;&#160;&#160;

 <B>Status: </B>open
 &#160;&#160;&#160;

 <B>Submitter: </B>Daniel Kr&#252;gler
 &#160;&#160;&#160;

 <B>Date: </B>2011-10-03<BR>


<P>(From messages <A href="
   http://accu.org/cgi-bin/wg21/message?wg=core&amp;msg=20571">20571</A> through
<A href="
   http://accu.org/cgi-bin/wg21/message?wg=core&amp;msg=20590">20590</A>.)
</P>

<P>Paragraphs 11 and 23 of 12.8 [class.copy] make a
defaulted move constructor and assignment operator, respectively,
deleted if there is a subobject with no corresponding move
function and for which the copy operation is non-trivial.  This
seems excessive and unnecessary.  For example:</P>

<PRE>
    template&lt;typename T&gt;
     struct wrap
     {
      wrap() = default;

    #ifdef USE_DEFAULTED_MOVE
      wrap(wrap&amp;&amp;) = default;
    #else
      wrap(wrap&amp;&amp; w) : t(static_cast&lt;T&amp;&amp;&gt;(w.t)) { }
    #endif

      wrap(const wrap&amp;) = default;

      T t;
     };

    struct S {
      S(){}
      S(const S&amp;){}
      S(S&amp;&amp;){}
    };

    typedef wrap&lt;const S&gt; W;

    W get() { return W(); }  //<SPAN style="font-family:Times;font-style:italic"> Error, if </SPAN>USE_DEFAULTED_MOVE<SPAN style="font-family:Times;font-style:italic"> is defined, else OK</SPAN>
</PRE>

<P>In this example the defaulted move constructor of
<TT>wrap</TT> is selected by overload resolution, but this
move-constructor is deleted, because <TT>S</TT> has no
<I>trivial</I> copy-constructor.</P>

<P>I think that we overshoot here with the delete rules: I see no
problem for the defaulted move-constructor in this example. Our
triviality-deduction rules already cover this case (12.8 [class.copy] paragraph 12: <TT>W::W(W&amp;&amp;)</TT> is not
trivial) and our exception-specification rules (15.4 [except.spec] paragraph 14) already correctly deduce a
<TT>noexcept(false)</TT> specification for
<TT>W::W(W&amp;&amp;)</TT>.</P>

<P>It would still be OK to prevent that a move-constructor
would be generated for the following example where no
user-declared defaulted copy/move members are present:</P>

<PRE>
    template&lt;typename T&gt;
     struct wrap_2
     {
      wrap_2() = default;
      T t;
     };

    typedef wrap_2&lt;const S&gt; W2;

    W2 get() { return W2(); }  //<SPAN style="font-family:Times;font-style:italic"> OK, selects copy constructor</SPAN>
</PRE>

<P>if we want. This would mean that we add a new bullet
to 12.8 [class.copy] paragraph 9 and paragraph 20.</P>

<h3>Drafting note</h3>

After extensive discussion on the reflector, in EWG and over lunch with
Daveed, Howard and Lawrence, I think we've settled on two points:

<ul>
  <li>An ill-formed move operation should not interefere with a valid copy
  operation.  I have implemented this by saying that a defaulted and
  deleted move operation is ignored by normal overload resolution; another
  way to handle this would be to say that it is not declared at all, even
  if the user wrote an explicitly defaulted declaration.</li>
  <li>There are no additional restrictions for implicit/defaulted move
  operations relative to copy.  This means that move assignment in a
  virtual base is dangerous (and compilers should probably warn) and
  implicit move assignment means that copy constructing an rvalue now only
  gives the weak exception safety guarantee, whereas in C++03 it gave the
  strong guarantee.  But we decided in Batavia that we weren't going to
  preserve all C++03 code against implicit move operations, and I think
  this resolution provides significant performance benefits.</li>
</ul>

<h3>Proposed Resolution</h3>

Change 12.8&para;9-11:

<blockquote>
If the definition of a class X does not explicitly declare a move constructor, one will be implicitly declared
as defaulted if and only if
<ul><li>X does not have a user-declared copy constructor,</li>
<li>X does not have a user-declared copy assignment operator,</li>
<li>X does not have a user-declared move assignment operator, <ins>and</ins></li>
<li>X does not have a user-declared destructor<ins>.</ins><del>, and</del></li>
<li><del>the move constructor would not be implicitly defined as
deleted.</del></li>
</ul>

[ Note: When the move constructor is not implicitly declared or explicitly
supplied, expressions that otherwise would have invoked the move
constructor may instead invoke a copy constructor. &mdash; end note ]

<p/>The implicitly-declared move constructor for class X will have the form
<blockquote><pre>X::X(X&&)</pre></blockquote> 

An implicitly-declared copy/move constructor is an inline public member of
its class. A defaulted copy/move constructor for a class X 
is defined as deleted (8.4.3) if X has:
<ul><li>a variant member with a non-trivial corresponding constructor and X
is a union-like class,</li>
<li>a non-static data member of class type M (or array thereof) that cannot
be copied/moved because overload resolution (13.3), as applied to M's
corresponding constructor, results in an ambiguity or a function that is
deleted or inaccessible from the defaulted constructor,</li>
<li>a direct or virtual base class B that cannot be copied/moved because
overload resolution (13.3), as applied to B's corresponding constructor,
results in an ambiguity or a function that is deleted or inaccessible from
the defaulted constructor,</li>
<li>any direct or virtual base class or non-static data member of a type
  with a destructor that is deleted or inaccessible from the defaulted
  constructor, <ins>or</ins></li>
<li>for the copy constructor, a non-static data member of rvalue reference type<ins>.</ins><del>, or</del></li>
<li><del>for the move constructor, a non-static data member or direct or
    virtual base class with a type that does not have a move constructor and
    is not trivially copyable.</del></li></ul>

<ins>A defaulted move constructor that is defined as deleted is ignored by
overload resolution (13.3, 13.4).  [ Note: A deleted move constructor would
otherwise interfere with initialization from an rvalue which can use the
copy constructor instead. --end note ]</ins>
</blockquote>

Change 12.8&para;20-23:

<blockquote>
If the definition of a class X does not explicitly declare a move assignment operator, one will be implicitly
declared as defaulted if and only if
<ul>
  <li>X does not have a user-declared copy constructor,</li>
  <li>X does not have a user-declared move constructor,</li>
  <li>X does not have a user-declared copy assignment operator, <ins>and</ins></li>
  <li>X does not have a user-declared destructor<ins>.</ins> <del>, and</del></li>
  <li><del>the move assignment operator would not be implicitly defined as deleted.</del></li>
</ul>
[ Example: The class definition
<blockquote><pre>struct S {
  int a;
  S& operator=(const S&) = default;
};</pre></blockquote>
will not have a default move assignment operator implicitly declared because the copy assignment operator has been user-declared. The move assignment operator may be explicitly defaulted.
<blockquote><pre>struct S {
  int a;
  S& operator=(const S&) = default;
  S& operator=(S&&) = default;
};</pre></blockquote>
&mdash; end example ]
<p/>The implicitly-declared move assignment operator for a class X will have the form
<blockquote><pre>X& X::operator=(X&&);</pre></blockquote>

<p/>The implicitly-declared copy/move assignment operator for class X has the return type X&; it returns the
object for which the assignment operator is invoked, that is, the object assigned to. An implicitly-declared
copy/move assignment operator is an inline public member of its class.

<p/>A defaulted copy/move assignment operator for class X is
defined as deleted if X has:
<ul>
  <li>a variant member with a non-trivial corresponding assignment operator and X is a union-like class, or</li>
  <li>a non-static data member of const non-class type (or array thereof), or</li>
  <li>a non-static data member of reference type, or</li>
<li>a non-static data member of class type M (or array thereof) that cannot
be copied/moved because overload resolution (13.3), as applied to M's
corresponding assignment operator, results in an ambiguity or a function
that is deleted or inaccessible from the defaulted assignment operator,
or</li>
<li>a direct or virtual base class B that cannot be copied/moved because
overload resolution (13.3), as applied to B's corresponding assignment
operator, results in an ambiguity or a function that is deleted or
inaccessible from the defaulted assignment operator<ins>.</ins><del>, or</del></li>
<li><del>for the move assignment operator, a non-static data member or
direct base class with a type that does not have a move assignment operator
and is not trivially copyable, or any direct or indirect virtual base
class.</del></li>
</ul>

<ins>A defaulted move assignment operator that is defined as deleted is
ignored by overload resolution (13.3, 13.4).</ins>
</blockquote>

<strong>Add to the end of 13.3.1:</strong>
<blockquote>
<ins>A defaulted move constructor or assignment operator (12.8) that is
defined as deleted is excluded from the set of candidate functions in all
contexts.</ins>
</blockquote>

</body>
</html>
