Default Move Functions

ISO/IEC JTC1 SC22 WG21 N2583 = 08-0093 - 2008-03-16

Lawrence Crowl, Lawrence@Crowl.org, crowl@google.com

Introduction

Exploiting move semantics requires writing a move constructor or a move assignment operator. As these functions have significant common implementations, the ability to specify default implementations for them would improve programmer productivity.

Proposal

We propose to use the =default syntax of N2346 Defaulted and Deleted Functions to specify a default implementation.

Semantically, the default implementation of the move constructor is a recursive move construction of its subobjects. In the event that there is no move constructor defined for a subobject, the copy constructor will be used instead.

The default implementation of the move assignment operator has the corresponding semantics.

For example,


class Derived
:
    public Base
{
    std::vector<int> vec;
    std::string name;
public:
    Derived(Derived&& x)
    :
        Base(static_cast<Base&&>(x)),
        vec(static_cast<std::vector<int>&&>(x.vec)),
        name(static_cast<std::string&&>(x.name))
    { }

    Derived& operator=(Derived&& x)
    {
        Base::operator=(static_cast<Base&&>(x));
        vec  = static_cast<std::vector<int>&&>(x.vec);
        name = static_cast<std::string&&>(x.name);
        return *this;
    }
};

There seems to be no reason to require that move constructors and move assignment operators be trivial, and so we do not.

A item of insecurity is the handling of pointer member variables. The insecurity is that the ownership of the indirect object is not readily visible in the source code, and no one default will be appropriate in all cases. The possible approaches are:

  1. Make the default move ill-formed, requiring the programmer to be clear about the handling of the pointer. This approach is a burden to programmers for which one of the other approaches is adequate. However, this approach is upwards compatible with the other approaches and is thus the least risky approach.
  2. Define the default move to copy pointer variables. This approach leaves indirect objects with multiple references, so freeing those objects becomes difficult. Under this approach, fully default moves are equivalent to fully default copies. This approach has the simplest normative text.
  3. Define the default move to copy pointer variables and null out the corresponding variable in the move argument. This approach avoids multiple references for owned objects, but may leave the argument object in a state not expected by the destructor. The most likely action in a destructor is to simply delete the pointer, so nulling out a pointer variable still leaves the object in an 'expected' state for such destructors because one may delete a null pointer. This approach has the most fidelity to move semantics.

The handling of reference member variables has much the same issue, except that pointer approach 3 (null parameter pointer member variables) is not possible for references. Thus, the reference approaches are:

  1. Make the default move ill-formed.
  2. Define the default move to copy reference variables. This approach is possible only for move construction, as reference variables cannot be assigned.

Wording

The proposed wording shows changes from an editing draft preliminary to the March 2008 mailing. The base text does not form an officially accepted draft. Earlier drafts have slightly different text.

Note that the edits to section 8.4, 8.4.1 and 8.4.2 are identical in this paper and in N2584 Default Swap Functions.

8.4 Function definitions [dcl.fct.def]

End the section after paragraph 8.

8.4.1 Explicitly-defaulted definitions [dcl.fct.def.default]

Add a new section comprising paragraph 9 of the old 8.4 Function definitions [dcl.fct.def].

Split the paragraph into three paragraphs, with the first paragraph containing the introductory sentence, the second paragraph containing the special member discussion, and the third paragraph containing the note and example.

Edit the resulting second paragraph as follows.

Only special Special member functions may be explicitly defaulted, and the implementation shall define them as if they had implicit definitions (12.1, 12.4, 12.8). A special member function that would be implicitly defined as deleted shall not be explicitly defaulted. A special member function is user-provided if it is user-declared and not explicitly defaulted on its first declaration. A user-provided explicitly-defaulted function is defined at the point where it is explicitly defaulted.

8.4.1.1 Move function defaults [dcl.fct.def.move]

Add a new section with the following paragraphs.

A move constructor has no implicit default, but may be explicitly defaulted. The default definition shall move construct its bases and variables, with arguments statically cast to a rvalue references (8.3.2 [dcl.ref]) from the corresponding bases and variables of the move constructor parameter. In the event that a base or variable has no move constructor, the copy constructor is used instead, without the static cast.

A move assignment operator has no implicit default, but may be explicitly defaulted. The default definition shall move assign its bases and variables, with arguments statically cast to a rvalue references (8.3.2 [dcl.ref]) from the corresponding bases and variables of the move assignment operator parameter. In the event that a base or variable has no move assignment operator, the copy assignment operator is used instead, without the static cast.

If pointer approach 1 is adopted, add the following paragraph.

If the default implementation of a move function would move from a pointer variable, the use of the move function default is ill-formed.

If pointer approach 2 is adopted, no additional text is needed.

If pointer approach 3 is adopted, add the following paragraph.

If the default implementation of a move function moves from a pointer variable, the implementation shall assign null to that pointer variable.

If reference approach 1 is adopted, add the following paragraph.

If the default implementation of a move function would move to a reference variable, the use of the move function default is ill-formed.

If reference approach 2 is adopted, add the following paragraph.

If the default implementation of a move assignment operator would move to a reference variable, the use of the move function default is ill-formed.

8.4.2 Deleted definitions [dcl.fct.def.delete]

Add a new section comprising paragraph 10 of the old 8.4 Function definitions [dcl.fct.def].

Unknown Sections

The working draft currently uses the term "move constructor" but does not define the term. It is unclear where to add the definition, move constructors are not a special member function, but construction is most completely described in Chapter 12 Special member functions [special].

A move constructor is a constructor with a declaration of the form

classname(classname&&);

The same issue applies to move assignment operators.

A move assignment operator is an assignment operator with a declaration of the form

classname& operator=(classname&&);