Document number: N2979=09-0169

Daniel Krügler
2009-11-04

Moving Swap Forward (revision 1)

Contents

Introduction

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

Swap, Forward, and Move

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

The forward function is needed to transfer the exact argument lvalueness information to another calling context and supports the so-called perfect forwarding idiom.

Finally, for the enablement of move-semantics an explicit cast is required to intentionally convert even lvalues to a corresponding rvalue-reference by means of the move protocol.

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. given the declaration

  template<class T>
  T&& value(); // not used

as part of the function template declaration

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

or as part of a class template definition

  template<class> class result_of;

  template<class Fn, class... ArgTypes>
  struct result_of<Fn(ArgTypes...)> 
  {
    typedef decltype(value<Fn>()(value<ArgTypes>()...)) type;
  };

The role of the function template value() is 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 its declaration to

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

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

Revision History

  1. N2979 - Revision 1
  2. N2958 - Initial version

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 its components.

However, the similarly short and descriptive name <value> was recently suggested by Walter Brown and should be considered, because all of move, forward, swap, and value are intimately related to values. The author would like to get feedback, which name would be preferred.

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 its 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 its 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. The fourth option, to simply provide no definition at all and to rely on linker diagnostics to make any abuse obvious, was considered as a bad choice because of the late diagnostics. After feedback from core language experts it was decided that it should be feasible to require that the code is ill-formed, if the function is used, as demonstrated by the following prototype implementation:

  template<class T>
  struct value_protector { 
    static const bool stop = false;
    static typename std::add_rvalue_reference<T>::type delegate(); // undefined
  };

  template<class T>
  typename std::add_rvalue_reference<T>::type value() {
    static_assert(value_protector<T>::stop, "value() must not be used!");
    return value_protector<T>::delegate();
  }

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 its semantics 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.

Possible Extensions

Besides the here proposed additions to the standard library further extensions of the presented ideas were brought to the author's consideration, among of these were two functions rvalue() and lvalue(),

  template<class T>
  T& lvalue(); // not used

  template<class T>
  typename add_rvalue_reference<typename remove_reference<T>::type>::type rvalue(); // not used

that could be considered as specialized versions of value(), because they would explicitly name the wanted lvalueness of the return type. Until now, no convincing examples came up that would indicate that these would be of equivalent usefulness, so this paper does not suggest to add them. Should indeed interest exist, they could be added later.

Bjarne Stroustrup suggested to add the function rval() with the same parameter type list, return type, and semantics as the existing move() function, because of the misleading character of the name of the latter (it doesn't move at all). The author believes that such an addition should be done preferrably now and not later, but would like to ask for feedback from other committee members regarding the preferred direction.

Proposed resolution

The following suggested wording bases on the numbering of N2960.

[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. Adapt the text of 17.6.1.2 [headers]/2 and add one further entry to Table 13 - C++ library headers ([headers]):

    The C++ standard library provides 5051 C++ library headers, as shown in Table 13.

    Table 13 - C++ library headers
    ... ... ... ... ...
    ... <initializer_list> <memory> <stack> ...
    ... <iomanip> <move> <stdexcept> ...
    ... ... ... ... ...

  2. Add one further entry to Table 15 - C++ headers for freestanding implementations ([using.overview]):

    Table 15 - C++ headers for freestanding implementations
    Subclause Header(s)
    ... ...
    18.10 Other runtime support <cstdarg>
    20.3  Move and forward functions <move>
    20.6  Type traits <type_traits>

  3. 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.2 Requirements  
    20.3 Move and forward functions <move>
    ... ...

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

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

    20.3 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.3.1, forward/move:
      template <class T, class U> T&& forward(U&& u);
      template <class T> MT move(T&&);
    
      // 20.3.2, value:
      template <class T> VT value(); // for unused context
    
      // 20.3.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.3.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);
    
    Returns: t.

    Remarks: The return type MT shall be defined equivalent to typename remove_reference<T>::type&&.

    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.3.2 Function template value [value]

    The library provides the function template value to simplify the definition of 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(); // for unused context
    
    Remarks: The return type VT shall be defined equivalent to typename add_rvalue_reference<T>::type. If the function is used according to [basic.def.odr], the program shall be ill-formed.

    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 its other components. Second, the references by numbers are replaced by references by ID's, third, 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 semantics is intended but the introduction of the normative "equivalent to" phrase seems to fix a current wording impreciseness.]
    20.3.3 Swap function templates [swap]
    
    template<class T>
    void swap(T& a, T& b);
    

    Requires: Type T shall be MoveConstructible ([moveconstructible]) and MoveAssignable ([moveassignable]).

    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.

  6. Change [utilities] as indicated:

    [NB: Neither move nor forward were part of TR1, therefore including header <move> here was dropped.]

    Header <utility> synopsis

    #include <initializer_list>
    
    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&&);
      ...
    }
    
  7. Remove the complete clause 20.3.2 [forward].

  8. 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]

  9. Change the entry for common_type in Table 51 — Other transformations ([meta.trans.other]):

    [NB: This wording change extends the type domain of common_type for cv void => cv void transformations and thus makes common_type usable for all binary type combinations that are supported by is_convertible]

    Table 51 — Other transformations
    Template Condition Comments
    template <class... T> struct common_type;   The member typedef type shall be defined as set out below. All types in the parameter pack T shall be complete or (possibly cv-qualified) void. A program may specialize this trait if at least one template parameter in the specialization is a user-defined type. [ Note:: Such specializations are needed when only explicit conversions are desired among the template arguments. — end note ]

  10. 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 its usefulness for cv void types as outlined above]

    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;
    };
    ...
    
  11. 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.3.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);
      ...
     }
    
  12. Change 25.3.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, Howard Hinnant and Walter Brown for their careful suggestions which really helped improving this document, Jens Maurer for his help clearing up some core language subtleties regarding unused entities, and Joe Gottman for his active support to get compile-time errors for any (mis)use of value(). Gabriel Dos Reis, Christopher Jefferson, Bjarne Stroustrup, and Ville Voutilainen participated in discussions and suggested improvements.