1290. Don't require [u|bi]nary_function inheritance

Section: 23.14 [function.objects] Status: Resolved Submitter: Daniel Krügler Opened: 2009-12-14 Last modified: 2017-03-22

Priority: Not Prioritized

View all other issues in [function.objects].

View all issues with Resolved status.

Discussion:

This issue is a follow-up of the discussion on issue 870 during the 2009 Santa Cruz meeting.

The class templates unary_function and binary_function are actually very simple typedef providers,

namespace std {

template <class Arg, class Result>
struct unary_function {
 typedef Arg argument_type;
 typedef Result result_type;
};

template <class Arg1, class Arg2, class Result>
struct binary_function {
 typedef Arg1 first_argument_type;
 typedef Arg2 second_argument_type;
 typedef Result result_type;
};

}

which may be used as base classes (similarly to the iterator template), but were originally not intended as a customization point. The SGI documentation introduced the concept Adaptable Unary Function as function objects "with nested typedefs that define its argument type and result type" and a similar definition for Adaptable Binary Function related to binary_function. But as of TR1 a protocol was introduced that relies on inheritance relations based on these types. 23.14.5 [refwrap]/3 b. 3 requires that a specialization of reference_wrapper<T> shall derive from unary_function, if type T is "a class type that is derived from std::unary_function<T1, R>" and a similar inheritance-based rule for binary_function exists as well.

As another disadvantage it has been pointed out in the TR1 issue list, N1837 (see section 10.39), that the requirements of mem_fn 23.14.12 [func.memfn]/2+3 to derive from std::unary_function/std::binary_function under circumstances, where the provision of corresponding typedefs would be sufficient, unnecessarily prevent implementations that take advantage of empty-base-class optimizations.

Both requirements should be relaxed in the sense that the reference_wrapper should provide typedef's argument_type, first_argument_type, and second_argument_type based on similar rules as the weak result type rule (23.14.3 [func.require]/3) does specify the presence of result_type member types.

For a related issue see also 1279.

[ 2010-10-24 Daniel adds: ]

Accepting n3145 would resolve this issue as NAD editorial.

[ 2010-11 Batavia: Solved by N3198 ]

Resolved by adopting n3198.

Previous proposed resolution:

[ The here proposed resolution is an attempt to realize the common denominator of the reflector threads c++std-lib-26011, c++std-lib-26095, and c++std-lib-26124. ]

  1. Change [base]/1 as indicated: [The intend is to provide an alternative fix for issue 1279 and some editorial harmonization with existing wording in the library, like 99 [iterator.basic]/1]

    1 The following class templates are provided to simplify the definition of typedefs of the argument and result types for function objects. The behavior of a program that adds specializations for any of these templates is undefined.:

    namespace std {
     template <class Arg, class Result>
     struct unary_function {
       typedef Arg argument_type;
       typedef Result result_type;
     };
    }
    
    namespace std {
     template <class Arg1, class Arg2, class Result>
     struct binary_function {
       typedef Arg1 first_argument_type;
       typedef Arg2 second_argument_type;
       typedef Result result_type;
     };
    }
    
  2. Change 23.14.5 [refwrap], class template reference_wrapper synopsis as indicated: [The intent is to remove the requirement that reference_wrapper derives from unary_function or binary_function if the situation requires the definition of the typedefs argument_type, first_argument_type, or second_argument_type. This change is suggested, because the new way of definition uses the same strategy as the weak result type specification applied to argument types, which provides the following advantages: It creates less potential conflicts between [u|bi]nary_function bases and typedefs in a function object and it ensures that user-defined function objects which provide typedefs but no such bases are handled as first class citizens.]

    namespace std {
     template <class T> class reference_wrapper
       : public unary_function<T1, R> // see below
       : public binary_function<T1, T2, R> // see below
     {
     public :
       // types
       typedef T type;
       typedef see below result_type; // not always defined
       typedef see below argument_type; // not always defined
       typedef see below first_argument_type; // not always defined
       typedef see below second_argument_type; // not always defined
    
       // construct/copy/destroy
       ...
     };
    
  3. Change 23.14.5 [refwrap]/3 as indicated: [The intent is to remove the requirement that reference_wrapper derives from unary_function if the situation requires the definition of the typedef argument_type and result_type. Note that this clause does concentrate on argument_type alone, because the result_type is already ruled by p. 2 via the weak result type specification. The new way of specifying argument_type is equivalent to the weak result type specification]

    3 The template instantiation reference_wrapper<T> shall be derived from std::unary_function<T1, R>define a nested type named argument_type as a synonym for T1 only if the type T is any of the following:

    • a function type or a pointer to function type taking one argument of type T1 and returning R
    • a pointer to member function R T0::f cv (where cv represents the member function's cv-qualifiers); the type T1 is cv T0*
    • a class type that is derived from std::unary_function<T1, R>with a member type argument_type; the type T1 is T::argument_type
  4. Change 23.14.5 [refwrap]/4 as indicated: [The intent is to remove the requirement that reference_wrapper derives from binary_function if the situation requires the definition of the typedef first_argument_type, second_argument_type, and result_type. Note that this clause does concentrate on first_argument_type and second_argument_type alone, because the result_type is already ruled by p. 2 via the weak result type specification. The new way of specifying first_argument_type and second_argument_type is equivalent to the weak result type specification]

    The template instantiation reference_wrapper<T> shall be derived from std::binary_function<T1, T2, R>define two nested types named first_argument_type and second_argument_type as a synonym for T1 and T2, respectively, only if the type T is any of the following:

    • a function type or a pointer to function type taking two arguments of types T1 and T2 and returning R
    • a pointer to member function R T0::f(T2) cv (where cv represents the member function's cv-qualifiers); the type T1 is cv T0*
    • a class type that is derived from std::binary_function<T1, T2, R>with member types first_argument_type and second_argument_type; the type T1 is T::first_argument_type and the type T2 is T::second_argument_type
  5. Change 23.14.12 [func.memfn]/2+3 as indicated: [The intent is to remove the requirement that mem_fn's return type has to derive from [u|bi]nary_function. The reason for suggesting the change here is to better support empty-base-class optimization choices as has been pointed out in N1837]

    2 The simple call wrapper shall be derived from std::unary_function<cv T*, Ret>define two nested types named argument_type and result_type as a synonym for cv T* and Ret, respectively, when pm is a pointer to member function with cv-qualifier cv and taking no arguments, where Ret is pm's return type.

    3 The simple call wrapper shall be derived from std::binary_function<cv T*, T1, Ret>define three nested types named first_argument_type, second_argument_type, and result_type as a synonym for cv T*, T1, and Ret, respectively, when pm is a pointer to member function with cv-qualifier cv and taking one argument of type T1, where Ret is pm's return type.

Proposed resolution:

Addressed by paper n3198.