1255. declval should be added to the library

Section: 22.2 [utility] Status: C++11 Submitter: Daniel Krügler Opened: 2009-11-03 Last modified: 2016-01-28

Priority: Not Prioritized

View all other issues in [utility].

View all issues with C++11 status.

Discussion:

During the Santa Cruz meeting it was decided to split off the provision of the library utility value() proposed in N2979 from the concrete request of the UK 300 comment. The provision of a new library component that allows the production of values in unevaluated expressions is considered as important to realize constrained templates in C++0x where concepts are not available.

The following proposed resolution is an improvement over that suggested in N2958, because the proposed component can now be defined without loss of general usefulness and any use by user-code will make the program ill-formed. A possible prototype implementation that satisfies the core language requirements can be written as:

template<class T>
  struct declval_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 declval() {
  static_assert(declval_protector<T>::stop, "declval() must not be used!");
  return declval_protector<T>::delegate();
}

Further-on the earlier suggested name value() has been changed to declval() after discussions with committee members.

Finally the suggestion shown below demonstrates that it can simplify existing standard wording by directly using it in the library specification, and that it also improves an overlooked corner case for common_type by adding support for cv void.

[ 2009-11-19 Moved to Tentatively Ready after 6 positive votes on c++std-lib. ]

Proposed resolution:

[ The proposed resolution has been updated to N3000 numbering and wording ]

  1. Change 22.2 [utility], header <utility> synopsis as indicated:

    // 20.3.3, forward/move:
    template <class T> struct identity;
    template <class T, class U> T&& forward(U&&);
    template <class T> typename remove_reference<T>::type&& move(T&&);
    
    // 20.3.4, declval:
    template <class T> typename add_rvalue_reference<T>::type declval(); // as unevaluated operand
    
  2. Immediately after the current section 22.2.4 [forward] insert a new section:

    20.3.4 Function template declval [declval]

    The library provides the function template declval to simplify the definition of expressions which occur as unevaluated operands (7 [expr]). The template parameter T of declval may be an incomplete type.

    template <class T> typename add_rvalue_reference<T>::type declval(); // as unevaluated operand
    

    Remarks: If this function is used according to 6.3 [basic.def.odr], the program is ill-formed.

    [Example:

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

    declares a function template convert, which only participates in overloading if the type From can be explicitly cast to type To. For another example see class template common_type (21.3.8.7 [meta.trans.other]). — end example]

  3. This bullet just makes clear that after applying N2984, the changes in 21.3.5.4 [meta.unary.prop], before table Type property queries should not use declval, because the well-formedness requirement of the specification of is_constructible would become more complicated, because we would need to make sure that the expression CE is checked in an unevaluated context.

  4. Also 21.3.7 [meta.rel]/4 is not modified similar to the previous bullet, because with the stricter requirements of not using declval() the well-formedness condition would be harder to specify. The following changes are only editorial ones (e.g. the removal of the duplicate declaration of create()):

    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:

    template <class T>
    typename add_rvalue_reference<T>::type create();
    To test() {
      return create<From>();
    }
    
  5. Change the entry in column "Comments" for common_type in Table 51 — Other transformations (21.3.8.7 [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 ]

    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]

  6. Change 21.3.8.7 [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 ? __tdeclval<T>() : __udeclval<U>()) type;
    };
    
  7. Change 99 [func.ret]/1 as indicated [This part solves some main aspects of issue 1225]:

    namespace std {
      template <class> class result_of; // undefined
    
      template <class Fn, class... ArgTypes>
      class result_of<Fn(ArgTypes...)> {
      public :
        // types
        typedef see belowdecltype(declval<Fn>() ( declval<ArgTypes>()... )) type;
      };
    }
    

    1 Given an rvalue fn of type Fn and values t1, t2, ..., tN of types T1, T2, ..., TN in ArgTypes, respectively, the type member is the result type of the expression fn(t1, t2, ...,tN). The values ti are lvalues when the corresponding type Ti is an lvalue-reference type, and rvalues otherwise.