Document number: N2958=09-0148

Daniel Krügler
2009-09-24

Moving Swap Forward

Contents

Introduction

The functions forward, move, and swap are rather fundamental cornerstones which are near to language facilities instead of library features:

Swap

The swap function is an old friend from C++03, and became recently extended to be applicable to native arrays with still some room for even more generality (see e.g. issue 742) for C++0x. The species swap can often be found in the neighbourhood of move-semantics, e.g. a typical move assignment operator can be defined this way:

  Moveable& Moveable::operator=(Moveable&& rhs) 
  {
    using std::swap;
    swap(member1, rhs.member1);
    swap(member2, rhs.member2);
    return *this;
  }

So swap has the very fundamental role of exchanging two things which is intimately related to the basics of copying and moving.

Forward

Due to the powerful perfect forwarding idiom, which was one of the most important reasons for the introduction of rvalue-references, the forward function is needed to transfer the exact argument lvalueness information to the next layer with the following characteristic pattern:

  template<class T>
  void foo(T&& arg) 
  {
    bar(std::forward<T>(arg));
  }

Move

Another important use-case for rvalue-references is the enablement of move-semantic in C++. Rvalues can immediately be moved, but a similar need often exists for moving lvalues, if they are no longer needed. For type-safety reasons those need an explicit cast to a corresponding rvalue-reference and that is what move has to realize:

  void sink(std::string&&);

  int main() 
  {
    std::string s = ...;
    ... // Do something with s
    sink(std::move(s)); // Transform s into an rvalue and let it move away
  }

Value

With the provision of decltype, late-specified return types, and default template-arguments for function templates a new generation of SFINAE patterns will emerge to at least partially compensate the lack of concepts on the C++0x timescale. Using this technique, it is sometimes necessary to obtain an object of a known type in a non-using context, e.g.

  template<class T>
  T&& value(); // not used, therefore not defined
	
  template<class To, class From>
  auto convert(From&&) -> decltype(static_cast<To>(value<From>()));

The role of the undefined function template value() is therefore a transformation of a type T into a value without using or evaluating this function. The name is supposed to direct the reader's attention to the fact that the expression value<T>() is an lvalue if and only if T is an lvalue-reference, otherwise an rvalue. To extend the domain of this function we can do a bit better by changing it's declaration to

  template<class T>
  typename std::add_rvalue_reference<T>::type value(); // not used, therefore not defined

which ensures that we can also use cv void as template parameter. The careful reader might have noticed that value() already exists under the name create() as part of the definition of the semantic of the type trait is_convertible in [meta.rel]/4.

All four presented functions are extremely light-weight, but it is expected that they will be part of the daily tool-box of the C++0x programmer.

Motivation

The three existing functions are located in the two headers <utility> and <algorithm>, which are both quite huge. This means that using these extremely fundamental functions always introduces a large overhead of code.

This negative aspect has lead to the national body comment UK 300 and this paper attempts to provide a solution for that, including an addition of a facility which according to the author's assumptions is of similar fundamentability and usefulness in template code.

The basic approach suggested in this paper is

Several naming suggestions for the header had been thought of - among these were <transfer> and <moving> - the currently proposed name is simply <move> following one prominent tradition of the standard headers to name the header equal to the name of one of it's components.

Similarly, several naming alternatives for value were also considered, e.g. decl (suggested by Alisdair Meredith), create, make, or fwd. The name value has the advantage of not causing a confusion with already existing names (forward, decltype), it also emphasizes better it's actual intention - the effect of attempting to get an rvalue or an lvalue - over make or create.

Several mechanisms were considered to ensure that an unguarded programmer cannot too easily misuse the undefined function value. It is impossible to mark it as an deleted function, because that would produce unwanted compile-time errors or silent removals from the overload set, depending on the context of it's occurrence. It was considered to mark the function with the attribute [[noreturn]], but the author has the suspicion that this might produce unwanted warnings of compilers or other diagnostic tools also for the intended use-cases and still wouldn't prevent using it. A third option was thought of, namely to provide indeed a definition which would either cause termination or the raise of an exception. This idea was rejected, because such a misuse might reveal too late in the software development process. In the end it seemed the best compromise to simply provide no definition at all and to rely on linker diagnostics to make any abuse obvious. On the positive side such scenarios seem less likely compared to an imprudent usage of another undefined function that would need no explicit template parameter for a valid call expression.

The proposed wording attempts to prevent direct coupling to other components by using words that explain effects and semantics instead of code, e.g. the swap overload for arrays should have the same effect as a call to swap_ranges, but that should not imply that <algorithm> should be included as well. Similarly should the declaration of value() not imply the inclusion of the complete <type_traits> header just because it's semantic depends on the semantic of add_rvalue_reference. To prevent existing libraries to perform heroic efforts to satisfy this intention, the wording uses no binding request, but instead a non-normative, encouraging wording style.

Proposed resolution

The following suggested wording bases on the numbering of N2723.

[The following wording assumes that N2951 has been applied. This proposal does not depend on the suggested resolution of N2951, but it simplifies the wording here. If N2951 would be rejected, this wording need only an update that ensures that the equivalent semantic effect of identity is realized]

  1. Add one further entry to Table 30 - General utilities library summary ([utilities]) immediately before <utility>:

    Table 30 — General utilities library summary
    Subclause Header(s)
    20.1 Requirements  
    20.2 Move and forward functions <move>
    ... ...

  2. Change [utilities.general]/2:

    The following subclauses describe utility and allocator requirements, move/forward utilities, utility components, compile-time rational arithmetic, tuples, type traits templates, function objects, dynamic memory management utilities, and date/time utilities, as summarized in Table 30.

  3. Add a new clause just before [utility]:

    20.2 Move and forward functions [move.forward]

    This subclause describes components used by C++ programs, particularly in templates, to perform fundamental operations related to moving, forwarding, and swapping objects.

    Header <move> synopsis

    namespace std {
    
      // 20.2.1, forward/move:
      template <class T, class U> T&& forward(U&& u);
      template <class T> MT move(T&&);
    
      // 20.2.2, value:
      template <class T> VT value(); // intentionally undefined
    
      // 20.2.3, swap:
      template <class T> void swap(T& a, T& b);
      template <class T, size_t N> void swap(T (&a)[N], T (&b)[N]);
    
    }
    

    20.2.1 forward/move helpers [forward]

    The library provides templated helper functions to simplify applying move semantics to an lvalue and to simplify the implementation of forwarding functions.

    [NB: The following is a relocation of the N2951 wording from [forward] around p. 3. Additionally a Notes clause is added to signal implementors that they should not include the header <type_traits> here:]

    template <class T, class U> T&& forward(U&& u);
    

    Returns: static_cast<T&&>(u).

    Remarks: If the following constraints are not met, this signature shall not participate in overload resolution:

    • The type formed by remove_reference<U>::type* shall be implicitly convertible to the type remove_reference<T>::type*, and
    • if T is an lvalue reference type, then U shall be an lvalue reference type.

    Notes: Implementations are encouraged but not required to avoid including the header <type_traits> to realize this definition.

    [NB: The following is an unconditional relocation of the example from [forward]/5:]

    [Example:
    
    template <class T, class A1, class A2>
    shared_ptr<T> factory(A1&& a1, A2&& a2) {
      return shared_ptr<T>(new T(std::forward<A1>(a1), std::forward<A2>(a2)));
    }
    
    struct A {
      A(int&, const double&);
    };
    
    void g() {
      shared_ptr<A> sp1 = factory<A>(2, 1.414); // error: 2 will not bind to int&
      int i = 2;
      shared_ptr<A> sp2 = factory<A>(i, 1.414); // OK
    }
    
    In the first call to factory, A1 is deduced as int, so 2 is forwarded to A's constructor as an rvalue. In the second call to factory, A1 is deduced as int&, so i is forwarded to A's constructor as an lvalue. In both cases, A2 is deduced as double, so 1.414 is forwarded to A's constructor as an rvalue. — end example]
    [NB: The following is a relocation of move from [forward]/7-9 with one further change: The introduction of MT is supposed to signal implementors that they should not include the header <type_traits> here:]
    
    template <class T> MT move(T&& t);
    
    Requires: The return type MT shall be defined equivalent to typename remove_reference<T>::type&&.

    Returns: t.

    Notes: Implementations are encouraged but not required to avoid including the header <type_traits> to realize the definition of MT.

    [Example:

    
    template <class T, class A1>
    shared_ptr<T> factory(A1&& a1) {
      return shared_ptr<T>(new T(std::forward<A1>(a1)));
    }
    
    struct A {
      A();
      A(const A&); // copies from lvalues
      A(A&&); // moves from rvalues
    };
    
    void g() {
      A a;
      shared_ptr<A> sp1 = factory<A>(a); // "a" binds to A(const A&)
      shared_ptr<A> sp1 = factory<A>(std::move(a)); // "a" binds to A(A&&)
    }
    
    - In the first call to factory, A1 is deduced as A&, so a is forwarded as a non-const lvalue. This binds to the constructor A(const A&), which copies the value from a. In the second call to factory, because of the call std::move(a), A1 is deduced as A, so a is forwarded as an rvalue. This binds to the constructor A(A&&), which moves the value from a. — end example]
    [NB: Here starts the wording for the new library component value. The introduction of VT is supposed to signal implementors that they should not include the header <type_traits> here:]
    20.2.2 Function template value [value]

    The library provides the declaration but no definition for the function template value to simplify the definition of types via expressions in unevaluated and unused contexts ([basic.def.odr], [expr]). The template parameter T of value may be an incomplete type.

    
    template <class T> VT value(); // intentionally undefined
    
    Requires: The return type VT shall be defined equivalent to typename add_rvalue_reference<T>::type.

    Remarks: The behavior is undefined, if the function is used according to [basic.def.odr].

    Notes: Implementations are encouraged but not required to avoid including the header <type_traits> to realize the definition of VT.

    [Example:

    
    template<class To, class From>
    decltype(static_cast<To>(value<From>())) convert(From&&);
    

    declares a function template convert, which does only participate in overloading, if the type From can be explicitly casted to type Toend example]

    [NB: The following is a relocation of swap from [algorithms] with two further changes: First, the clause is named [swap] instead of [algo.swap], because the latter is supposed to be conserved for it's other components. Second, the semantic definition of the overload for arrays uses wording to encourage implementors not to include header <algorithm> here. Except for this no change in semantic is intended but the introduction of the normative "equivalent to" phrase seems to fix a current wording impreciseness.]
    20.2.3 Swap function templates [swap]
    
    template<class T>
    void swap(T& a, T& b);
    

    Requires: Type T shall be MoveConstructible (33) and MoveAssignable (35).

    Effects: Exchanges values stored in two locations.

    
    template<class T, size_t N>
    void swap(T (&a)[N], T (&b)[N]);	
    

    Effects: Equivalent to swap_ranges(a, a + N, b).

    Notes: Implementations are encouraged but not required to avoid including the header <algorithm> to realize the definition of this swap overload.

  4. Change [utilities] as indicated:

    [NB: The explicit addition of <move> ensures backward compatibility versus TR1]

    Header <utility> synopsis

    #include <initializer_list>
    #include <move>
    
    namespace std {
      ...
      // 20.3.2, forward/move:
      template <class T> struct identity;
      template <class T> T&& forward(typename identity<T>::type&&);
      template <class T> typename remove_reference<T>::type&& move(T&&);
      ...
    }
    
  5. Remove the complete clause 20.2.2 [forward].

  6. Change [meta.rel]/4 as indicated:

    [NB: This wording change is just an editorial simplification of the definition of is_convertible. No wording change is intended, but we use already the wording that was introduced with LWG defect 975]

    ... Given the following function prototype:

    
    template <class T> 
    typename add_rvalue_reference<T>::type create();
    
    tThe predicate condition for a template specialization is_convertible<From, To> shall be satisfied, if and only if the return expression in the following code would be well-formed, including any implicit conversions to the return type of the function.
    To test() { 
      return createvalue<From>(); 
    }
    
    [Note: This requirement gives well defined results for reference types, void types, array types, and function types. — end note]

  7. Change [meta.trans.other]/3 as indicated:

    [NB: This wording change is more than an editorial simplification of the definition of common_type: It also extends it's usefulness for the rare but possible situation of a cv void => cv void transformation and thus makes e.g. common_type usable for all binary type combinations that are supported by is_convertible]

    The nested typedef common_type::type shall be defined as follows:

    ...
    template <class T, class U>
    struct common_type<T, U> {
    private:
      static T&& __t();
      static U&& __u();
    public:
      typedef decltype(true ? __tvalue<T>() : __uvalue<U>()) type;
    };
    ...
    
  8. Change 25 [algorithms], header <algorithm> as indicated:

    [NB: The explicit addition of <move> ensures backward compatibility versus C++03]

    Header <algorithm> synopsis

    #include <move>
    
    namespace std {
      ...
      // 25.2.3, swap:
      template<class T> void swap(T& a, T& b);
      template<class T, size_t N> void swap(T (&a)[N], T (&b)[N]);
      template<class ForwardIterator1, class ForwardIterator2>
      ForwardIterator2 swap_ranges(ForwardIterator1 first1,
      ForwardIterator1 last1, ForwardIterator2 first2);
      template<class ForwardIterator1, class ForwardIterator2>
      void iter_swap(ForwardIterator1 a, ForwardIterator2 b);
      ...
     }
    
  9. Change 25.2.3 [alg.swap] as indicated:

    
    template<class T>
    void swap(T& a, T& b);
    

    Requires: Type T shall be MoveConstructible (33) and MoveAssignable (35).

    Effects: Exchanges values stored in two locations.

    
    template<class T, size_t N>
    void swap(T (&a)[N], T (&b)[N]);	
    

    Effects: swap_ranges(a, a + N, b).

Acknowledgements

The author would like to thank Alisdair Meredith for his encouragement to write this paper and for his inspiration to add the value function.

I would also like to acknowledge Howard Hinnant's careful suggestions which really helped improving this document.