tags:

views:

1164

answers:

4

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; 
 } 
 };
+11  A: 

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
You should note that these typedefs are used by the stl functional library, for example the negators not1 and not2.
Luc Touraille
@Luc: Thanks for notice.
Mykola Golubyev
+4  A: 

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
@litb: Good point about bind1st. Never used my object functors with bind1st.
Mykola Golubyev
+8  A: 

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
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.
+1  A: 

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