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

Deprecating unary_function and binary_function (Revision 1)

Addressed NB comments: GB 95

Addressed issues: LWG 1290, LWG 1279

Introduction

This paper attempts to resolve issues related to several currently required class-derivation relations from specializations of unary_function and binary_function.

For details of the rationale of this paper see n3145.

Discussion

During the Batavia 2010 meeting it was agreed on the general direction suggested by n3145. There was also one part of the proposal that did not reach consensus, namely the deprecation of the negators not1(), not2(), and their corresponding return types, because there are currently no actual replacement forms (except usage of lambda expressions).

This revision of n3145 does exclude this particular part, and adapts the current definitions of unary_negate and binary_negate in the same way as for the remaining affected function object types.

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, negators:
      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. Change the template definitions in sub-clause [negators] as indicated. The intent is to replace the derivation from binary_function and unary_function by correspondingly existing typedefs:

    
    template <class Predicate>
    class unary_negate
      : public unary_function<typename Predicate::argument_type,bool> {
    public:
      typedef typename Predicate::argument_type argument_type;
      typedef bool result_type;
      explicit unary_negate(const Predicate& pred);
      bool operator()(const typename Predicate::argument_type& x) const;
    };
    	
    [..]
    
    template <class Predicate>
    class binary_negate
      : public binary_function<typename Predicate::first_argument_type,
      typename Predicate::second_argument_type, bool> {
    public:
      typedef typename Predicate::first_argument_type first_argument_type;
      typedef typename Predicate::second_argument_type second_argument_type;
      typedef bool result_type;
      explicit binary_negate(const Predicate& pred);
      bool operator()(const typename Predicate::first_argument_type& x,
        const typename Predicate::second_argument_type& y) const;
    };	
    
  10. Following the new sub-clause [depr.lib.base] and before the current D.9 [depr.lib.binders] 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);
          }
        };
        
        [..]