Document number: N3145=10-0135
Date: 2010-10-06
Author: Daniel Krügler
Project: Programming Language C++, Library Working Group
Reply-to: Daniel Krügler

Deprecating unary_function and binary_function

Addressed NB comments: GB 95

Addressed issues: LWG 1290, LWG 1279

Introduction

The templates

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;
};

are types that were provided to solely simplify the definition of some canonical typenames in functor types. Unfortunately these library components were underspecified, and the current wording would basically allow to specialize them for any user-defined type, but the library does not clarify the restrictions of such specializations. Further-on during TR1 a protocol was introduced that based on inheritance-relations of function object types and several other components, like reference_wrapper, bind, function, and others.

Discussion

Our experience with concepts gives us confidence that it is rarely necessary to depend on specific base class-derived class relations, if availability of types and functions is sufficient. The new language tools allow us even in the absence of language-supported concepts to deduce the existence of typenames in class types, which would introduce a much weaker coupling among them. Another advantage of replacing inheritance by associated types is the fact, that this will reduce the number of cases, where ambiguities arise: This can easily happen, if a type would inherit both from unary_function and binary_function (This makes sense, if a functor is both an unary and a binary function object).

During the Rapperswil meeting consensus was found for deprecating the types unary_function and binary_function and the protocol that was using them. This proposal describes the details of such an approach.

Proposed resolution

The proposed wording changes refer to N3126.

  1. Change [function.objects]/2, header <functional> synopsis as indicated. The intent is to deprecate the adaptable function protocol and the adapter classes that are now much better represented by bind and lambda closures:

    namespace std {
      // 20.8.3[depr.lib.base], base (deprecated):
      template <class Arg, class Result> struct unary_function;
      template <class Arg1, class Arg2, class Result> struct binary_function;
    
      [..]
    
      // 20.8.9[depr.lib.negators], negators (deprecated):
      template <class Predicate> class unary_negate;
      template <class Predicate>
        unary_negate<Predicate> not1(const Predicate&);
      template <class Predicate> class binary_negate;
      template <class Predicate>
        binary_negate<Predicate> not2(const Predicate&);
    
      [..]
      
      // 20.8.11[depr.lib.funptr.adaptors], adaptors (deprecated):
      template <class Arg, class Result> class pointer_to_unary_function;
      template <class Arg, class Result>
        pointer_to_unary_function<Arg,Result> ptr_fun(Result (*)(Arg));
      template <class Arg1, class Arg2, class Result>
        class pointer_to_binary_function;
      template <class Arg1, class Arg2, class Result>
        pointer_to_binary_function<Arg1,Arg2,Result>
          ptr_fun(Result (*)(Arg1,Arg2));
    
      // 20.8.12[depr.lib.memptr.adaptors], adaptors (deprecated): 
      template<class S, class T> class mem_fun_t; 
      template<class S, class T, class A> class mem_fun1_t; 
      template<class S, class T>
      	mem_fun_t<S,T> mem_fun(S (T::*f)()); 
      template<class S, class T, class A>
        mem_fun1_t<S,T,A> mem_fun(S (T::*f)(A)); 
      template<class S, class T> class mem_fun_ref_t; 
      template<class S, class T, class A> class mem_fun1_ref_t; 
      template<class S, class T> 
        mem_fun_ref_t<S,T> mem_fun_ref(S (T::*f)()); 
      template<class S, class T, class A>
      	mem_fun1_ref_t<S,T,A> mem_fun_ref(S (T::*f)(A));
      
      template <class S, class T> class const_mem_fun_t; 
      template <class S, class T, class A> class const_mem_fun1_t; 
      template <class S, class T> 
      	const_mem_fun_t<S,T> mem_fun(S (T::*f)() const); 
      template <class S, class T, class A>
      	const_mem_fun1_t<S,T,A> mem_fun(S (T::*f)(A) const); 
      template <class S, class T> class const_mem_fun_ref_t; 
      template <class S, class T, class A> class const_mem_fun1_ref_t; 
      template <class S, class T> 
        const_mem_fun_ref_t<S,T> mem_fun_ref(S (T::*f)() const); 
      template <class S, class T, class A> 
      	const_mem_fun1_ref_t<S,T,A> mem_fun_ref(S (T::*f)(A) const);
    	
      [..]
    }	
    

  2. Remove sub-clause [base] and insert a new sub-clause just before the current D.9 [depr.lib.binders] as follows:

    20.8.3 Base [base]

    1 The following classes are provided to simplify the typedefs of the argument and result types:

    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;
      };
    }
    

    D.?? Base [depr.lib.base]

    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;
      };
    }
    

  3. Change [refwrap] as indicated. The intent is to replace the inheritance requirement by corresponding typedef requirements:

    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
        [..]  
      };
    }
    

  4. Change [refwrap]/3+4 as indicated. The intent is to replace the currently required inheritance-rules by associated type-rules:

    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:

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

  5. Change the template definitions in sub-clause [arithmetic.operations] as indicated. The intent is to replace the derivation from binary_function and unary_function by correspondingly existing typedefs:

    template <class T> struct plus : binary_function<T,T,T> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef T result_type;
      T operator()(const T& x, const T& y) const;
    };	
    
    [..]
    
    template <class T> struct minus : binary_function<T,T,T> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef T result_type;
      T operator()(const T& x, const T& y) const;
    };	
    
    [..]
    	
    template <class T> struct multiplies : binary_function<T,T,T> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef T result_type;
      T operator()(const T& x, const T& y) const;
    };	
    
    [..]
    
    template <class T> struct divides : binary_function<T,T,T> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef T result_type;
      T operator()(const T& x, const T& y) const;
    };	
    
    [..]
    
    template <class T> struct modulus : binary_function<T,T,T> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef T result_type;
      T operator()(const T& x, const T& y) const;
    };	
    
    [..]
    
    template <class T> struct negate : unary_function<T,T> {
      typedef T argument_type;
      typedef T result_type;
      T operator()(const T& x) const;
    };	
    
  6. Change the template definitions in sub-clause [comparisons] as indicated. The intent is to replace the derivation from binary_function by correspondingly existing typedefs:

    template <class T> struct equal_to : binary_function<T,T,bool> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef bool result_type;
      bool operator()(const T& x, const T& y) const;
    };	
    
    [..]
    
    template <class T> struct not_equal_to : binary_function<T,T,bool> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef bool result_type;
      bool operator()(const T& x, const T& y) const;
    };	
    
    [..]
    
    template <class T> struct greater : binary_function<T,T,bool> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef bool result_type;
      bool operator()(const T& x, const T& y) const;
    };	
    
    [..]
    
    template <class T> struct less : binary_function<T,T,bool> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef bool result_type;
      bool operator()(const T& x, const T& y) const;
    };	
    
    [..]
    
    template <class T> struct greater_equal : binary_function<T,T,bool> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef bool result_type;
      bool operator()(const T& x, const T& y) const;
    };	
    
    [..]
    
    template <class T> struct less_equal : binary_function<T,T,bool> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef bool result_type;
      bool operator()(const T& x, const T& y) const;
    };	
    
  7. Change the template definitions in sub-clause [logical.operations] as indicated. The intent is to replace the derivation from binary_function and by unary_function correspondingly existing typedefs:

    template <class T> struct logical_and : binary_function<T,T,bool> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef bool result_type;
      bool operator()(const T& x, const T& y) const;
    };	
    
    [..]
    
    template <class T> struct logical_or : binary_function<T,T,bool> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef bool result_type;
      bool operator()(const T& x, const T& y) const;
    };	
    
    [..]
    
    template <class T> struct logical_not : unary_function<T,bool> {
      typedef T argument_type;
      typedef bool result_type;
      bool operator()(const T& x) const;
    };	
    
  8. Change the template definitions in sub-clause [bitwise.operations] as indicated. The intent is to replace the derivation from binary_function by correspondingly existing typedefs:

    template <class T> struct bit_and : binary_function<T,T,T> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef T result_type;
      T operator()(const T& x, const T& y) const;
    };	
    
    [..]
    
    template <class T> struct bit_or : binary_function<T,T,T> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef T result_type;
      T operator()(const T& x, const T& y) const;
    };	
    
    [..]
    	
    template <class T> struct bit_xor : binary_function<T,T,T> {
      typedef T first_argument_type;
      typedef T second_argument_type;
      typedef T result_type;
      T operator()(const T& x, const T& y) const;
    };	
    
  9. Move the complete contents of sub-clause 20.8.9 Negators [negators] as a new sub-clause just after the new sub-clause [depr.lib.base] and before the current D.9 [depr.lib.binders] starting with:

    D.?? Negators [depr.lib.negators]

    1 Negators not1 and not2 take a unary and a binary predicate, respectively, and return their complements (5.3.1).

      template <class Predicate>
      class unary_negate
        : public unary_function<typename Predicate::argument_type,bool> {
    

    [..] // Remaining parts from previous 20.8.9 Negators [negators]

  10. Following the new sub-clause Negators [depr.lib.negators] (see previous bullet). add a new sub-clause [depr.lib.adaptors] and insert two further children sub-clauses, [depr.lib.funptr.adaptors] and [depr.lib.memptr.adaptors]. Move the complete contents of sub-clause 20.8.11 Adaptors for pointers to functions [function.pointer.adaptors] as contents of the new sub-clause [depr.lib.funptr.adaptors]. Move the complete contents of sub-clause 20.8.12 Adaptors for pointers to members [member.pointer.adaptors] as contents of the new sub-clause [depr.lib.memptr.adaptors].:

    D.?? Adaptors [depr.lib.adaptors]

    The adaptors ptr_fun, mem_fun, mem_fun_ref, and their corresponding return types are deprecated. [ Note: The function template bind (20.8.10) provides a better solution. — end note ]

    D.??.1 Adaptors for pointers to functions [depr.lib.funptr.adaptors]

    1 To allow pointers to (unary and binary) functions to work with function adaptors the library provides:

      template <class Arg, class Result>
      class pointer_to_unary_function : public unary_function<Arg, Result> {
    

    [..] // Remaining parts from previous 20.8.11 Adaptors for pointers to functions [function.pointer.adaptors]

    D.??.2 Adaptors for pointers to members [depr.lib.memptr.adaptors]

    1 The purpose of the following is to provide the same facilities for pointer to members as those provided for pointers to functions in 20.8.11.:

      template <class S, class T> class mem_fun_t
        : public unary_function<T*, S> {
    

    [..] // Remaining parts from previous 20.8.12 Adaptors for pointers to members [member.pointer.adaptors]

  11. Change [func.memfn]/2+3 as indicated. The intent is to replace the derivation from binary_function and unary_function by correspondingly existing typedefs:

    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.

  12. Change 20.8.14.2 [func.wrap.func], class template function synopsis as indicated. The intent is to replace the currently required inheritance-rules by associated type-rules:

    namespace std {
      template<class> class function; // undefined
    
      template<class R, class... ArgTypes>
      class function<R(ArgTypes...)>
        : public unary_function<T1, R> // iff sizeof...(ArgTypes) == 1 and ArgTypes contains T1
        : public binary_function<T1, T2, R> // iff sizeof...(ArgTypes) == 2 and ArgTypes contains T1 and T2
      {
      public:
        typedef R result_type;
        typedef T1 argument_type; // iff sizeof...(ArgTypes) == 1 and ArgTypes contains T1
        typedef T1 first_argument_type; // iff sizeof...(ArgTypes) == 2 and ArgTypes contains T1 as first type
        typedef T2 second_argument_type; // iff sizeof...(ArgTypes) == 2 and ArgTypes contains T2 as second type
    
        // 20.8.14.2.1, construct/copy/destroy:
        [..]
    

  13. Change 20.9.11.3.7 [util.smartptr.ownerless]/1, class template owner_less synopsis as indicated. The intent is to replace the currently required inheritance-rules by associated type-rules:

    1 The class template owner_less allows ownership-based mixed comparisons of shared and weak pointers.

    namespace std {
      template<class T> struct owner_less;
    
      template<class T> struct owner_less<shared_ptr<T> >
        : binary_function<shared_ptr<T>, shared_ptr<T>, bool>
      {
        typedef bool result_type;
        typedef shared_ptr<T> first_argument_type;
        typedef shared_ptr<T> second_argument_type;
        bool operator()(shared_ptr<T> const&, shared_ptr<T> const&) const;
        bool operator()(shared_ptr<T> const&, weak_ptr<T> const&) const;
        bool operator()(weak_ptr<T> const&, shared_ptr<T> const&) const;
      };
      
      template<class T> struct owner_less<weak_ptr<T> >
        : binary_function<weak_ptr<T>, weak_ptr<T>, bool>
      {
        typedef bool result_type;
        typedef weak_ptr<T> first_argument_type;
        typedef weak_ptr<T> second_argument_type;
        bool operator()(weak_ptr<T> const&, weak_ptr<T> const&) const;
        bool operator()(shared_ptr<T> const&, weak_ptr<T> const&) const;
        bool operator()(weak_ptr<T> const&, shared_ptr<T> const&) const;
      };
    }  
    

  14. Change 23.6.1 [map]/2, class template map::value_compare as indicated. The intent is to replace the currently required inheritance-rules by associated type-rules:

    namespace std {
      template <class Key, class T, class Compare = less<Key>,
        class Allocator = allocator<pair<const Key, T> > >
      class map {
      public:
        [..]
       
        class value_compare
          : public binary_function<value_type,value_type,bool> {
        friend class map;
        protected:
          Compare comp;
          value_compare(Compare c) : comp(c) {}
        public:
          typedef value_type first_argument_type;
          typedef value_type second_argument_type;
          typedef bool result_type;      
          bool operator()(const value_type& x, const value_type& y) const {
            return comp(x.first, y.first);
          }
        };
        
        [..]
    

  15. Change 23.6.2 [multimap]/2, class template multimap::value_compare as indicated. The intent is to replace the currently required inheritance-rules by associated type-rules:

    namespace std {
      template <class Key, class T, class Compare = less<Key>,
        class Allocator = allocator<pair<const Key, T> > >
      class multimap {
      public:
        [..]
       
        class value_compare
          : public binary_function<value_type,value_type,bool> {
        friend class multimap;
        protected:
          Compare comp;
          value_compare(Compare c) : comp(c) {}
        public:
          typedef value_type first_argument_type;
          typedef value_type second_argument_type;
          typedef bool result_type;      
          bool operator()(const value_type& x, const value_type& y) const {
            return comp(x.first, y.first);
          }
        };
        
        [..]