29

What is the benefit of inheriting from std::binary_function (or std::unary_function)?

For example I have such code:

class Person
{
 public:
    Person();
    Person(int a, std::string n);
    Person(const Person& src);

    int age;
    std::string name;
 };

 Person::Person()
           : age(0)
             , name("")
               {};

 Person::Person(int a, std::string n)
 : age(a)
 , name(n)
 {};

 Person::Person(const Person& src)
 {
   age = src.age;
   name = src.name;
 };

 struct PersonPrint : public std::unary_function<Person, void>{
   void operator() (Person p){
     std::cout << " Person age: " << p.age 
               << " name: " << p.name << std::endl;
   }
 };

 struct PersonGreater : public std::binary_function<Person, Person, bool>{
   bool operator()(const Person& p1, const Person p2){
     if (p1.age > p2.age) return true;
     if (p1.name.compare(p2.name) > 0) return true;
     return false;
   }
 };

 int main(int count, char** args)
 {
   std::vector<Person> personVec;
   Person p1(10, "Person1");
   Person p2(12, "Person2");
   Person p3(12, "Person3");

   personVec.push_back(p1);
   personVec.push_back(p2);
   personVec.push_back(p3);

   std::cout << "before sort: " << std::endl;
   std::for_each(personVec.begin(), personVec.end(), PersonPrint());
   std::sort(personVec.begin(), personVec.end(), PersonGreater());
   std::cout << "after: " << std::endl;
   std::for_each(personVec.begin(), personVec.end(), PersonPrint());
 }

But I also could write this code without inheritance form std::unary_function/std::binary_function?

 struct PersonPrint {
     void operator() (Person p) {
         std::cout << " Person age: " << p.age << " name: " << p.name << std::endl; 
     } 
 }; 

 struct PersonGreater {
     bool operator()(const Person& p1, const Person p2) {
         if (p1.age > p2.age) return true; 
         if (p1.name.compare(p2.name) > 0) return true; 
         return false; 
     } 
 };

UPDATED

std::binary_function and std::unary_function are deprecated as of C++11 see comment by @AlexandreC.

Darius Kucinskas
  • 10,193
  • 12
  • 57
  • 79
  • 2
    They have been deprecated in C++11 (of course, there was no C++11 at the time the question had been asked). – Alexandre C. Mar 20 '12 at 23:07
  • @AlexandreC. what should we use instead? in c++ coding standards, andrei mentionned that they are critical to construct functors to be used with stl algorithms – kirill_igum Jan 19 '13 at 23:44
  • @kirill_igum: Now that `decltype`, `auto` and `std::result_of` are available, you don't need to inherit anything. Also, `bind1st` and `bind2nd` have been deprecated in favor of `bind`. – Alexandre C. Jan 20 '13 at 00:03
  • @AlexandreC. so there is no need to create a functor instead of a function either? Earlier, the reason for functor was so that one can inherent from binary_function. – kirill_igum Jan 20 '13 at 00:11
  • @kirill_igum: a functor can store state, that a bare function cannot. – Alexandre C. Jan 20 '13 at 10:53
  • @AlexandreC. I meant just for functionality as a function. without storing the state. but since you didn't mention other reason, it sounds good to me. thanks – kirill_igum Jan 20 '13 at 21:17

4 Answers4

30

Inheritance from [unary|binary]_function just gives you an additional typedefs in your class:

For unary_function

argument_type
result_type

For binary_function

first_argument_type
second_argument_type
result_type 

Which are those types you pass to [unary|binary]_function. In your case there is no benefits.

If you ever going to use your Functors with other std Functors modificators like not1, bind1st you have to inherit from [unart|binart]_function.

And if you are going to store this template information for your purpose it is better to use ready solution.

Mykola Golubyev
  • 57,943
  • 15
  • 89
  • 102
16

Besides the typedefs (already mentioned), there is also as aspect of readability. When I see struct Foo {... my first thought will be "Foo is a type". But with struct Foo : public unary_function<... I already know that Foo is a functor. For a programmer (unlike compilers), types and functors are quite distinct.

MSalters
  • 173,980
  • 10
  • 155
  • 350
  • 7
    To be strict, Foo is never a functor. It is either a type, or, specifically, a functor *type*. Functors themselves are instantiations of functor types. –  Mar 04 '09 at 14:40
9

Like Mykola explains, they are just adding typedefs. Imagine for your PersonGreater, you want to fix the first argument to some person. The binder1st would need to store the first argument somewhere, and so it needs the type of the first argument. binary_function provides that as a typedef:

// get a function object that compares person1 against 
// another person
std::bind1st(PersonGreater(), person1)

Now, the returned binder1st object knows that the type of the argument it needs to store is of type Person.

Some function objects negate the result of another function object. Here we need the type of the argument too:

template <class Predicate>
class unary_negate
    : public unary_function<typename Predicate::argument_type,bool> {
    Predicate pred;
public:
    explicit unary_negate(const Predicate& pred):pred(pred) { }
    bool operator()(const typename Predicate::argument_type& x) const {
        return !pred(x);
    }
};

That could also use a templated operator(), but the Standard defines it to use the argument_type type as parameter. The negator itself is derived from unary_function and needs to provide the first argument type anyway.

Sometimes, people try to use [unary,binary]_function to store function objects/pointers. However, they cannot be used for that. boost::function fulfills that job and will be adopted in the next Standard as std::function.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212
3

It's a strong form of documentation enforced by the compiler.

By inheriting, you are making a promise that you will implement the binary_function interface, and the compiler will hold you to it. Then, clients can trust that your class can be used wherever a binary_function is needed.

JohnMcG
  • 8,709
  • 6
  • 42
  • 49