Document number: N3142=10-0132

Jason Merrill
Daniel Krügler
Howard Hinnant
Gabriel Dos Reis
2010-10-15

Adjustments to constructor and assignment traits

This paper proposes adjustments to the constructor and assignment traits, and to other closely related subjects. These changes address the following national body comments:

DE 17
DE 18
GB 91
GB 92
FI 18
GB 50
GB 51

Introduction

The library reflector thread starting with c++std-lib-28321 exposed confusion concerning the constructor and assignment traits. The current names lead people to believe that the semantics of the traits should match the core language semantics, and are confused when they don't.

But the core language semantics aren't actually very useful to a library writer; they don't care whether or not a class has something which the core language considers a copy constructor. They want to know if actually writing T(someT) will work, whether it calls a copy constructor or a move constructor or a member template constructor or whatever constructor.

This is particularly true of the "trivial" variants; it doesn't matter whether a class has a trivial copy constructor if the constructor you actually end up calling for a particular argument is non-trivial.

The is_constructible<T, Args...> trait on the other hand, is named in such a way that conveys the semantics most useful to a library writer: i.e. can I construct a T in this specific way.

Therefore we propose to change the names of all of the has_*_constructor and has_*_assign traits to is_*_constructible and is_*_assignable. This renaming is summarized in the following table:

Summary of renamed traits
General form Nothrow form Trivial form
Construction is_constructible is_nothrow_constructible  
hasis_default_constructibleor hasis_nothrow_default_constructibleor hasis_trivially_default_constructibleor
hasis_copy_constructibleor hasis_nothrow_copy_constructibleor hasis_trivially_copy_constructibleor
hasis_move_constructibleor hasis_nothrow_move_constructibleor hasis_trivially_move_constructibleor
Destruction     hasis_trivially_destructibleor
Assignment hasis_copy_assignable hasis_nothrow_copy_assignable hasis_trivially_copy_assignable
hasis_move_assignable hasis_nothrow_move_assignable hasis_trivially_move_assignable

Furthermore, as noted in GB 92 for the "nothrow" traits, the definitions have become redundant and confusing. This redundancy and confusion actually extends to nearly all of these traits, and in some cases actually leads to incorrect definitions, or at the very least, a lack of self-consistency among the traits. For example, as specified in the current Working Draft:

typedef char Array[3];
is_constructible<Array, const Array&>::value == false
has_copy_constructor<Array, const Array&>::value == true

With our proposed clarifying names, we wish for is_copy_constructible<T> to have the exact same behavior as is_constructible<T, const T&>. So we have adjusted the definitions of the is_*_constructible traits to be simple synonyms for is_constructible and similarly for the "nothrow" variants. E.g.:

template <class T> struct has_nothrow_copy_constructor is_nothrow_copy_constructible; has_trivial_copy_constructor<T>::value is true or is_nothrow_constructible<U, const U&>::value is true, where U is remove_all_extents<T>::type.
is_nothrow_constructible<T, const T&>::value is true.
T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.

This formulation makes it much easier to get the definitions correct. I.e. what it means to be "nothrow constructible" is defined in exactly one place (the definition of is_nothrow_constructible). The other "nothrow constructible" traits reuse this definition as much as possible, with no repetition.

However we note that we can not currently apply this formula to the "trivially constructible" traits, nor to is_trivially_destructible, nor to any of the assignable traits. The problem is the lack of is_destructible (FI 18), lack of is_trivially_constructible, and the lack of generalized assignable traits. We strongly feel that this lack of completeness will inevitably lead to situations where programmers will not be able to query or assert properties of types necessary to the correct operation of their code. For example a container is likely to assert that a contained type have a non-throwing destructor. Lack of this property leads to the container's destructor becoming a source of memory leaks.

Therefore we propose the following 6 new traits to complete the suite of traits:

Summary of traits, new traits shown as insertions
General form Nothrow form Trivial form
Construction is_constructible is_nothrow_constructible is_trivially_constructible
is_default_constructible is_nothrow_default_constructible is_trivially_default_constructible
is_copy_constructible is_nothrow_copy_constructible is_trivially_copy_constructible
is_move_constructible is_nothrow_move_constructible is_trivially_move_constructible
Destruction is_destructible is_nothrow_destructible is_trivially_destructible
Assignment is_assignable is_nothrow_assignable is_trivially_assignable
is_copy_assignable is_nothrow_copy_assignable is_trivially_copy_assignable
is_move_assignable is_nothrow_move_assignable is_trivially_move_assignable

We address GB 91 in the definition of the newly introduced is_assignable trait, using declval as suggested. All of the rest of the assignable traits reuse this definition without repetition.

In addition we wish to address GB 50 and GB 51 in this paper because the topic is intimately related to the definition of move construction and move assignment and subsequently the is_move_constructible and is_move_assignable traits.

The following list shows the current usages of the terms "move-construct" and "move-assign" in the library. While some occurrences are purely descriptive usages of these words, most of them refer to what the type traits is_move_constructible<T> and is_move_assignable<T> stand for. The list omits to enumerate all usages of these terms within the definition of pair and tuples, because those will be completely replaced by another paper (N3140).

  1. Table 42, entry: X::propagate_on_container_move_assignment

    true_type only if an allocator of type X should be moved when the client container is move-assigned.
  2. 20.8.14.2.1/8: "otherwise, move-constructs the target"
  3. 20.9.10.2.1/14: "This unique_ptr will hold a value move constructed from d."
  4. 20.9.10.2.1/19: "If D is a reference type, this deleter is copy constructed from u's deleter; otherwise, this deleter is move constructed from u's deleter."
  5. 20.9.10.2.1/24: "If E is a reference type, this deleter is copy constructed from u's deleter; otherwise, this deleter is move constructed from u's deleter."
  6. 20.9.11.2.1/24: "Effects: Move-constructs a shared_ptr instance from r."
  7. Table 93 — Container requirements, entry "a = rv": "All existing elements of a are either move assigned to or destroyed"
  8. Table 96 — Allocator-aware container requirements (continued), entry "X(rv) X u(rv)": "Requires: move construction of A shall not exit via an exception."
  9. Table 96 — Allocator-aware container requirements (continued), entry "a = rv":

    All existing elements of a are either move assigned to or destroyed.
  10. 23.5.2.1/2: "Effects: Initializes comp with x and c with y (copy constructing or move constructing as appropriate);"
  11. 23.5.2.1/4: "Effects: Initializes comp with x and c with y (copy constructing or move constructing as appropriate);"
  12. 26.7.4/before p.1: "assigns the result to *(result + (i - first)), and move assigns from val to acc."
  13. 27.7.1.1.1/3: "Effects: Move constructs from the rvalue rhs. This is accomplished by [..]" (Defined)
  14. 27.7.1.5.1/3: "Effects: Move constructs from the rvalue rhs by constructing the basic_istream base class with move(rhs)." (Defined)
  15. 27.7.2.2/5: "Effects: Move constructs from the rvalue rhs. This is accomplished by [..]" (Defined)
  16. 27.8.1.1/4: "Effects: Move constructs from the rvalue rhs. [..]" (Defined)
  17. 27.8.1.2/1: "Effects: After the move assignment [..]" (Descriptive)
  18. 27.8.2.1/3 "Move constructs from the rvalue rhs. This is accomplished by move constructing the base class, and the contained basic_stringbuf. [..]"
  19. 27.8.3.2/1 "Effects: Move assigns the base and members of *this from the base and corresponding members of rhs."
  20. 27.8.5/3: "Move constructs from the rvalue rhs. This is accomplished by move constructing the base class, and the contained basic_stringbuf. [..]"
  21. 27.8.5.1/1: "Effects: Move assigns the base and members of *this from the base and corresponding members of rhs."
  22. 27.9.1.2/3: "Effects: Move constructs from the rvalue rhs." (Descriptive)
  23. 27.9.1.3/1: "Effects: Calls this->close() then move assigns from rhs." (Descriptive)
  24. 27.9.1.7/4: "Effects: Move constructs from the rvalue rhs. This is accomplished by move constructing the base class, and the contained basic_filebuf. [..]"
  25. 27.9.1.8/1: "Effects: Move assigns the base and members of *this from the base and corresponding members of rhs." (Descriptive)
  26. 27.9.1.11/4: "Effects: Move constructs from the rvalue rhs. This is accomplished by move constructing the base class, and the contained basic_filebuf. [..]"
  27. 27.9.1.12/1 "Effects: Move assigns the base and members of *this from the base and corresponding members of rhs."
  28. 27.9.1.15/4: "Effects: Move constructs from the rvalue rhs. This is accomplished by move constructing the base class, and the contained basic_filebuf. [..]"
  29. 27.9.1.16/1: "Effects: Move assigns the base and members of *this from the base and corresponding members of rhs."
  30. 28.8.2/12: "Effects: Move constructs an object of class basic_regex from e." (Descriptive)
  31. 28.8.3/9: "Effects: move assigns from that into *this and returns *this." (Descriptive)
  32. 28.10.1/5: "Effects: Move-constructs an object of class match_results from m satisfying the same postconditions as Table 138." (Descriptive) "Additionally, the stored Allocator value is move constructed from m.get_allocator()." (Defined meaning)
  33. 28.10.1/8: "Effects: Move-assigns m to *this. The postconditions of this function are indicated in Table 138." (Descriptive)
  34. 30.6.6/7: "Effects: move constructs a future object that refers to the associated asynchronous state that was originally referred to by rhs (if any)." (Descriptive)
  35. 30.6.7/10: "Effects: move constructs a shared_future object that refers to the associated asynchronous state that was originally referred to by rhs (if any)." (Descriptive)

Proposed Wording

  1. Insert a new definition just before [defns.move.assign]:

    17.3.?? [defns.move.constr]
    move construction
    Direct-initialization of an object of some type with an rvalue of the same type.
  2. Change [defns.move.assign] as indicated. We disagree with the NB comment suggestion GB 50 to add a cross-reference to (12.8). The reason is that as of the recent core wording changes move and copy special member functions rely on overload resolution. Second, even if we use the proper initialization or assignment syntax with an rvalue (e.g. via std::move) this won't guarantee that a corresponding move operation is really called. If absent, a copy operation will be considered. Therefore we suggest to add definitions that are more in agreement with the expression-based approach also used for the traits definitions:

    17.3.12 [defns.move.assign]
    move assignment
    use of a move assignment operator Assignment operation of an rvalue of some object type to an modifiable lvalue of the same type..
  3. Remove the following two definitions. They are superfluous in agreement with the NB comments GB 50+GB 51:

    17.3.13 [defns.move.assign.op]
    move assignment operator
    an assignment operator which accepts only an rvalue argument of the type being assigned to and might modify the argument as a side effect during the assignment.
     
    17.3.14 [defns.move.ctor]
    move constructor
    a constructor which accepts only an rvalue argument of the type being constructed and might modify the argument as a side effect during construction.
  4. Change the signature of move_if_noexcept in the synopsis in 20.3 [utility] and in 20.3.3 [forward]:

    template <class T>
    typename conditional<
        !has_nothrow_move_constructor<T>::value && has_copy_constructor<T>::value,
        !is_nothrow_move_constructible<T>::value && is_copy_constructible<T>::value,
        const T&,
        T&&
    >::type
    move_if_noexcept(T& x);
    
  5. Update the synopsis in 20.7.2 [meta.type.synop] as indicated. We suggest a slight reordering of two traits in the header synopsis to make all three groups consistent as shown below.

    template <class T, class... Args> struct is_constructible;
    template <class T, class... Args> struct is_nothrow_constructible;
    template <class T>                struct hasis_default_constructibleor;
    template <class T>                struct hasis_copy_constructibleor;
    template <class T>                struct is_move_constructible;
    template <class T, class U>       struct is_assignable;
    template <class T>                struct hasis_copy_assignable;
    template <class T>                struct has_move_constructor;
    template <class T>                struct hasis_move_assignable;
    template <class T>                struct is_destructible;
    
    template <class T, class... Args> struct is_trivially_constructible;
    template <class T>                struct hasis_trivially_default_constructibleor;
    template <class T>                struct hasis_trivially_copy_constructibleor;
    template <class T>                struct hasis_trivially_move_constructibleor;
    template <class T, class U>       struct is_trivially_assignable;
    template <class T>                struct hasis_trivially_copy_assignable;
    template <class T>                struct hasis_trivially_move_assignable;
    template <class T>                struct hasis_trivially_destructibleor;
    
    template <class T, class... Args> struct is_nothrow_constructible;
    template <class T>                struct hasis_nothrow_default_constructibleor;
    template <class T>                struct hasis_nothrow_copy_constructibleor;
    template <class T>                struct hasis_nothrow_move_constructibleor;
    template <class T, class U>       struct is_nothrow_assignable;
    template <class T>                struct hasis_nothrow_copy_assignable;
    template <class T>                struct hasis_nothrow_move_assignable;
    template <class T>                struct is_nothrow_destructible;
    
  6. Modify Table 45 — Type property predicates in 20.7.4.3 [meta.unary.prop] as indicated. The table has been reordered consistent with the synopsis. [Rationale: Text presented like this in the table is intended to be rationale for the change, and should not be put into the working paper.]

    Table 45 — Type property predicates
    Template Condition Preconditions
    ...
    template <class T, class... Args> struct is_constructible; see below T and all types in the parameter pack Args shall be complete types, (possibly cv-qualified) void, or arrays of unknown bound.
    template <class T> struct has_default_constructor is_default_constructible; is_constructible<UT>::value is true, where U is remove_all_extents<T>::type.

    [Rationale: There is no need to treat array types specially. Arrays can be default constructible as defined by is_constructible<T>]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T> struct has_copy_constructor is_copy_constructible; is_constructible<UT, const UT&>::value is true, where U is remove_all_extents<T>::type.

    [Rationale: There is no need to treat array types specially. Arrays are never copy constructible as defined by is_constructible<T, const T&>]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T> struct has_move_constructor is_move_constructible; is_constructible<UT, UT&&>::value is true, where U is remove_all_extents<T>::type.

    [Rationale: There is no need to treat array types specially. Arrays are never move constructible as defined by is_constructible<T, T&&>]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T, class U> struct is_assignable; The expression declval<T>() = declval<U>() is well-formed when treated as an unevaluated operand (Clause 5). Access checking is performed as if in a context unrelated to T and U. Only the validity of the immediate context of the assignment expression is considered. [Note: The compilation of the expression can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]

    [Rationale: Addresses GB 91 with the use of declval. Addresses the same concern DE 17 has with is_constructible and is_convertible. Addresses DE 18 with a detailed description of access checking. Due to the extensive reuse of is_assignable for the other assignable traits, the introduction of this trait automatically fixes all of the assignable traits with respect to GB 91, DE 17 and DE 18.]
    T and U shall be complete types, (possibly cv-qualified) void, or arrays of unknown bound.
    template <class T> struct has_copy_assign is_copy_assignable; T is neither const nor a reference type, and T is a trivial type (3.9) or the expression *(U*)0 = declval<const U&>() is well-formed when treated as an unevaluated operand (Clause 5), where U is remove_all_extents<T>::type. is_assignable<T&, const T&>::value is true.

    [Rationale: Addresses GB 91, DE 17 and DE 18 by reusing is_assignable.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T> struct has_move_assign is_move_assignable; T is neither const nor a reference type, and T is a trivial type (3.9) or the expression *(U*)0 = declval<U>() is well-formed when treated as an unevaluated operand (Clause 5), where U is remove_all_extents<T>::type. is_assignable<T&, T&&>::value is true.

    [Rationale: Addresses GB 91, DE 17 and DE 18 by reusing is_assignable.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T> struct is_destructible; For a complete type T and given template <class T> struct test {T t;};, test<T>::~test() is not deleted.

    [Rationale: Partially addresses FI 18, and addresses the same access concern DE 18 has with the existing traits. By addressing DE 18 once here, and having the other destructible traits reuse this definition, DE 18 is addressed automatically for the other destructible traits.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T, class... Args> struct is_trivially_constructible; is_constructible<T, Args...>::value is true and the variable definition for is_constructible, as defined below, is known to call no operation that is not trivial (3.9 [basic.types], 12 [special]).

    [Rationale: Serves to define the common characteristics of all of the "trivially constructible" traits in one place. Reuses is_constructible so as to address the concerns of DE 17 and DE 18.]
    T and all types in the parameter pack Args shall be complete types, (possibly cv-qualified) void, or arrays of unknown bound.
    template <class T> struct has_trivial_default_constructor is_trivially_default_constructible; T is a trivial type (3.9) or a class type with a trivial default constructor (12.1) or an array of such a class type. is_trivially_constructible<T>::value is true.

    [Rationale: Reuses is_trivially_constructible so as to address the concerns of DE 17 and DE 18.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T> struct has_trivial_copy_constructor is_trivially_copy_constructible; T is a trivial type (3.9) or a reference type or a class type whose copy constructors (12.8) are all trivial. is_trivially_constructible<T, const T&>::value is true.

    [Rationale: Reuses is_trivially_constructible so as to address the concerns of DE 17 and DE 18.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T> struct has_trivial_move_constructor is_trivially_move_constructible; T is a trivial type (3.9) or a reference type or a class type whose move constructors (12.8) are all trivial. is_trivially_constructible<T, T&&>::value is true.

    [Rationale: Reuses is_trivially_constructible so as to address the concerns of DE 17 and DE 18.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T, class U> struct is_trivially_assignable; is_assignable<T, U>::value is true and the assignment, as defined by is_assignable, is known to call no operation that is not trivial (3.9 [basic.types], 12 [special]).

    [Rationale: Reuses is_assignable so as to address the concerns of GB 91, DE 17 and DE 18.]
    T and U shall be complete types, (possibly cv-qualified) void, or arrays of unknown bound.
    template <class T> struct has_trivial_copy_assign is_trivially_copy_assignable; T is neither const nor a reference type, and T is a trivial type (3.9) or a class type whose copy assignment operators (12.8) are all trivial. is_trivially_assignable<T&, const T&>::value is true.

    [Rationale: Reuses is_trivially_assignable so as to address the concerns of GB 91, DE 17 and DE 18.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T> struct has_trivial_move_assign is_trivially_move_assignable; T is neither const nor a reference type, and T is a trivial type (3.9) or a class type whose move assignment operators (12.8) are all trivial. is_trivially_assignable<T&, T&&>::value is true.

    [Rationale: Reuses is_trivially_assignable so as to address the concerns of GB 91, DE 17 and DE 18.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T> struct has_trivial_destructor is_trivially_destructible; T is a trivial type (3.9) or a reference type or a class type with a trivial destructor (12.4) or an array of such a class type. is_destructible<T>::value is true and the indicated destructor is known to be trivial.

    [Rationale: Reuses is_destructible so as to address the concerns of DE 18.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T, class... Args> struct is_nothrow_constructible; is_constructible<T, Args...>::value is true and the variable definition for is_constructible, as defined below, is known not to throw any exceptions (5.3.7 [expr.unary.noexcept]).

    [Rationale: We rely on the core language meaning here and therefore suggest to add a reference to 5.3.7 [expr.unary.noexcept], because this allows us to implicitly point to the noexcept operator as a tool to recognize this property in situations where expressions are involved. Some type traits are defined in terms of variable definitions and we therefore cannot directly use the operator for them. A correct implementation will be based on a compiler-intrinsic here, simulating implementations will typically partition into different expressions, each of them could be checked via noexcept.
    Referring to 5.3.7 [expr.unary.noexcept] has the advantage that this sub-clause directly refers to 15.1 [except.throw] where we find
    Throwing an exception transfers control to a handler.
    In the context we use the term it is clear that this means that the final handler is not located within the expression or definition that is the result of an exception. ]
    T and all types in the parameter pack Args shall be complete types, (possibly cv-qualified) void, or arrays of unknown bound.
    template <class T> struct has_nothrow_default_constructor is_nothrow_default_constructible; has_trivial_default_constructor<T>::value is true or is_nothrow_constructible<U>::value is true, where U is remove_all_extents<T>::type. is_nothrow_constructible<T>::value is true.

    [Rationale: Reuses is_nothrow_constructible so as to get the details of the "no throw" characteristic correct and defined in only one place. Addresses GB 92 by removing the redundant and confusing reference to has_trivial_default_constructor. There is no need to treat array types specially.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T> struct has_nothrow_copy_constructor is_nothrow_copy_constructible; has_trivial_copy_constructor<T>::value is true or is_nothrow_constructible<U, const U&>::value is true, where U is remove_all_extents<T>::type. is_nothrow_constructible<T, const T&>::value is true.

    [Rationale: Reuses is_nothrow_constructible so as to get the details of the "no throw" characteristic correct and defined in only one place. Addresses GB 92 by removing the redundant and confusing reference to has_trivial_copy_constructor. There is no need to treat array types specially. Array types are never copy constructible.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T> struct has_nothrow_move_constructor is_nothrow_move_constructible; has_trivial_move_constructor<T>::value is true or is_nothrow_constructible<U, U&&>::value is true, where U is remove_all_extents<T>::type. is_nothrow_constructible<T, T&&>::value is true.

    [Rationale: Reuses is_nothrow_constructible so as to get the details of the "no throw" characteristic correct and defined in only one place. Addresses GB 92 by removing the redundant and confusing reference to has_trivial_move_constructor. There is no need to treat array types specially. Array types are never move constructible.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T, class U> struct is_nothrow_assignable; is_assignable<T, U>::value is true and the assignment is known not to throw any exceptions (5.3.7 [expr.unary.noexcept]).

    [Rationale: Reuses is_assignable to address GB 91, GB 92, DE 17 and DE 18. References [expr.unary.noexcept] for the same reasons as stated for is_nothrow_constructible.]
    T and U shall be complete types, (possibly cv-qualified) void, or arrays of unknown bound.
    template <class T> struct has_nothrow_copy_assign is_nothrow_copy_assignable; has_trivial_copy_assign<T>::value is true or the expression noexcept(*(U*)0 = declval<const U&>()) is well-formed and true, where U is remove_all_extents<T>::type. is_nothrow_assignable<T&, const T&>::value is true.

    [Rationale: Reuses is_nothrow_assignable to address GB 91, GB 92, DE 17 and DE 18 and to get the details of the "no throw" characteristic correct and defined in only one place.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T> struct has_nothrow_move_assign is_nothrow_move_assignable; has_trivial_move_assign<T>::value is true and T is a trivial type (3.9) or the expression noexcept(*(U*)0 = declval<U>()) is well-formed and true, where U is remove_all_extents<T>::type. is_nothrow_assignable<T&, T&&>::value is true.

    [Rationale: Reuses is_nothrow_assignable to address GB 91, GB 92, DE 17 and DE 18 and to get the details of the "no throw" characteristic correct and defined in only one place.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    template <class T> struct is_nothrow_destructible; is_destructible<T>::value is true and the indicated destructor is known to not throw any exceptions (5.3.7 [expr.unary.noexcept]).

    [Rationale: Addresses FI 18. Reuses is_destructible to address DE 18. References [expr.unary.noexcept] for the same reasons as stated for is_nothrow_constructible.]
    T shall be a complete type, (possibly cv-qualified) void, or an array of unknown bound.
    ...

    4 ...

    5 ...

    6 Given the following function prototype:

    template <class T>
      typename add_rvalue_reference<T>::type create();
    

    the predicate condition for a template specialization is_constructible<T, Args...> shall be satisfied if and only if the following variable definition would be well-formed for some invented variable t:

    T t(create<Args>()...);
    

    [Note: these tokens are never interpreted as a function declaration. — end note] Access checking is performed as if in a context unrelated to T and any of the Args. Only the validity of the immediate context of the variable initialization is considered. [Note: The evaluation of the initialization can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]

    [Rationale: The words used above for is_constructible, and also below for is_convertible address both DE 17 and DE 18. Due to the extensive reuse of is_constructible for the other constructible traits, the fix for is_constructible automatically fixes all of the constructible traits with respect to DE 17 and DE 18.]

  7. Modify Table 47 — Type relationship predicates in 20.7.5 [meta.rel] as indicated:

    Table 47 — Type relationship predicates
    Template Condition Comments
    ...
    template <class From, class To> struct is_convertible; see below From and To shall be complete types, arrays of unknown bound, or (possibly cv-qualified) void types.

    3 ...

    4 Given the following function prototype:

    template <class T>
      typename add_rvalue_reference<T>::type create();
    

    the 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 create<From>();
    }
    

    [Note: This requirement gives well defined results for reference types, void types, array types, and function types. — end note] Access checking is performed as if in a context unrelated to To and From. Only the validity of the immediate context of the expression of the return-statement (including conversions to the return type) is considered. [Note: The evaluation of the conversion can result in side effects such as the instantiation of class template specializations and/or function template specializations, the generation of implicitly-defined functions, etc. Such side effects are not in the "immediate context" and can result in the program being ill-formed. — end note]

Acknowledgements

Many thanks to Jens Maurer for his generous and insightful contributions.