Title
Move functions too often deleted
Status
cd3
Section
_N4750_.15.8 [class.copy]
Submitter
Daniel Krügler

Created on 2011-10-03.00:00:00 last changed 90 months ago

Messages

Date: 2013-04-15.00:00:00

[Moved to DR status at the April, 2013 meeting as paper N3667.]

Date: 2012-12-15.00:00:00

Proposed resolution (December, 2012):

  1. Change _N4750_.15.8 [class.copy] paragraph 9 as follows:

  2. 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

    • X does not have a user-declared copy constructor,

    • X does not have a user-declared copy assignment operator,

    • X does not have a user-declared move assignment operator, and

    • X does not have a user-declared destructor., and

    • the move constructor would not be implicitly defined as deleted.

    [Note:...

  3. Change _N4750_.15.8 [class.copy] paragraph 11 as follows:

  4. ...A defaulted copy/move constructor for a class X is defined as deleted (9.5.3 [dcl.fct.def.delete]) if X has:

    • a variant member with a non-trivial corresponding constructor and X is a union-like class,

    • a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (12.2 [over.match]), as applied to M's corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,

    • a direct or virtual base class B that cannot be copied/moved because overload resolution (12.2 [over.match]), as applied to B's corresponding constructor, results in an ambiguity or a function that is deleted or inaccessible from the defaulted constructor,

    • 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, or

    • for the copy constructor, a non-static data member of rvalue reference type., or

    • 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.

    A defaulted move constructor that is defined as deleted is ignored by overload resolution (12.2 [over.match]). [Note: A deleted move constructor would otherwise interfere with initialization from an rvalue which can use the copy constructor instead. —end note]

  5. Change _N4750_.15.8 [class.copy] paragraph 20 as follows:

  6. 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

    • X does not have a user-declared copy constructor,

    • X does not have a user-declared move constructor,

    • X does not have a user-declared copy assignment operator, and

    • X does not have a user-declared destructor., and

    • the move assignment operator would not be implicitly defined as deleted.

    [Example:...

  7. Change _N4750_.15.8 [class.copy] paragraph 23 as follows:

  8. A defaulted copy/move assignment operator for class X is defined as deleted if X has:

    • a variant member with a non-trivial corresponding assignment operator and X is a union-like class, or

    • a non-static data member of const non-class type (or array thereof), or

    • a non-static data member of reference type, or

    • a non-static data member of class type M (or array thereof) that cannot be copied/moved because overload resolution (12.2 [over.match]), 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

    • a direct or virtual base class B that cannot be copied/moved because overload resolution (12.2 [over.match]), 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., or

    • 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.

    A defaulted move assignment operator that is defined as deleted is ignored by overload resolution (12.2 [over.match], 12.3 [over.over]).

This resolution also resolves issue 1491.

Date: 2012-08-15.00:00:00

Additional notes (August, 2012):

The proposed resolution was extensively discussed and additional alternatives were suggested. A paper is being produced for the October, 2012 meeting describing the various options, so the issue has been returned to "review" status to wait for the outcome of that deliberation.

See also the discussion of issue 1491 for additional considerations.

Date: 2012-02-15.00:00:00

Proposed resolution (February, 2012):

  1. Change _N4750_.15.8 [class.copy] paragraph 9 as follows:

  2. 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

    • ...

    • X does not have a user-declared destructor, and

    • the move constructor would not be implicitly defined as deleted., and

    • each of X's non-static data members and direct or virtual base classes has a type that either has a move constructor or is trivially copyable.

    [Note:...

  3. Change _N4750_.15.8 [class.copy] paragraph 11 as follows:

  4. 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 (9.5.3 [dcl.fct.def.delete]) if X has:

    • ...

    • 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, or

    • for the copy constructor, a non-static data member of rvalue reference type., or

    • 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.

  5. Change _N4750_.15.8 [class.copy] paragraph 20 as follows:

  6. 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

    • ...

    • X does not have a user-declared destructor, and

    • the move assignment operator would not be implicitly defined as deleted.,

    • X has no direct or indirect virtual base class with a non-trivial move assignment operator, and

    • each of X's non-static data members and direct or virtual base classes has a type that either has a move assignment operator or is trivially copyable.

    Example:...

  7. Change _N4750_.15.8 [class.copy] paragraph 23 as follows:

  8. A defaulted copy/move assignment operator for class X is defined as deleted if X has:

    • ...

    • a direct or virtual base class B that cannot be copied/moved because overload resolution (12.2 [over.match]), 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., or

    • 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.

Date: 2020-12-15.00:00:00

Paragraphs 11 and 23 of _N4750_.15.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:

    template<typename T>
     struct wrap
     {
      wrap() = default;

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

      wrap(const wrap&) = default;

      T t;
     };

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

    typedef wrap<const S> W;

    W get() { return W(); }  // Error, if USE_DEFAULTED_MOVE is defined, else OK

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

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 (_N4750_.15.8 [class.copy] paragraph 12: W::W(W&&) is not trivial) and our exception-specification rules (14.5 [except.spec] paragraph 14) already correctly deduce a noexcept(false) specification for W::W(W&&).

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:

    template<typename T>
     struct wrap_2
     {
      wrap_2() = default;
      T t;
     };

    typedef wrap_2<const S> W2;

    W2 get() { return W2(); }  // OK, selects copy constructor

if we want. This would mean that we add a new bullet to _N4750_.15.8 [class.copy] paragraph 9 and paragraph 20.

History
Date User Action Args
2014-03-03 00:00:00adminsetstatus: drwp -> cd3
2013-10-14 00:00:00adminsetstatus: dr -> drwp
2013-05-03 00:00:00adminsetmessages: + msg4416
2013-05-03 00:00:00adminsetstatus: review -> dr
2013-01-14 00:00:00adminsetmessages: + msg4203
2012-09-24 00:00:00adminsetmessages: + msg3892
2012-09-24 00:00:00adminsetstatus: ready -> review
2012-02-27 00:00:00adminsetmessages: + msg3730
2012-02-27 00:00:00adminsetstatus: open -> ready
2011-10-03 00:00:00admincreate