2009-03-04 9 views
27

¿Cuál es el beneficio de la herencia de std :: binary_function (o std :: unary_function)?¿Cuál es el beneficio de heredar de std :: binary_function (o std :: unary function)?

Por ejemplo, he dicho código:

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

Pero también podría escribir el código sin herencia de formularios 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; 
    } 
}; 

ACTUALIZADO

std :: binary_function y std :: unary_function están en desuso a partir del C++ 11 Véase el comentario del @AlexandreC.

+2

Han quedado en desuso en C++ 11 (por supuesto, no había C++ 11 en el momento en que se formuló la pregunta). –

+0

@AlexandreC. ¿Qué deberíamos usar en su lugar? en los estándares de codificación C++, y mencioné que son fundamentales para construir funtores para ser utilizados con los algoritmos stl –

+0

@kirill_igum: Ahora que 'decltype',' auto' y 'std :: result_of' están disponibles, no es necesario heredar cualquier cosa. Además, 'bind1st' y' bind2nd' han quedado en desuso en favor de 'bind'. –

Respuesta

29

herencia de | _function [unario binaria] simplemente le da una typedefs adicionales en su clase:

Para unary_function

argument_type 
result_type 

Para binary_function

first_argument_type 
second_argument_type 
result_type 

Cuáles son esos tipos que pasas a [unary | binary] _function. En su caso no hay beneficios.

Si alguna vez va a utilizar sus Functors con otros modificadores std Functors como not1, bind1st debe heredar de [unart | binart] _function.

Y si se va a almacenar esta información de la plantilla para su propósito, es mejor utilizar una solución preparada.

+1

Debe tener en cuenta que estos typedefs son utilizados por la biblioteca funcional stl, por ejemplo, los negators not1 y not2. –

+0

@Luc: Gracias por avisarnos. –

9

Como Mykola explica, sólo están añadiendo typedefs. Imagínese para su PersonGreater, quiere arreglar el primer argumento para alguna persona. El binder1st necesitaría almacenar el primer argumento en alguna parte, por lo que necesita el tipo del primer argumento. binary_function establece que como un typedef:

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

Ahora, el objeto devuelto binder1st sabe que el tipo del argumento que necesita para almacenar es de tipo Person.

Algunos objetos de función niega el resultado de otro objeto función. Aquí tenemos que el tipo del argumento demasiado:

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

que también podría utilizar una plantilla operator(), pero la norma define a utilizar el tipo de argument_type como parámetro. El propio negator se deriva de la función unary y necesita proporcionar el primer tipo de argumento de todos modos.

A veces, la gente trata de usar [unary,binary]_function para almacenar objetos de función/punteros. Sin embargo, no pueden ser utilizados para eso. boost::function cumple con ese trabajo y será adoptado en el próximo Estándar como std::function.

+0

@litb: Buen punto sobre bind1st. Nunca utilicé mis funtores de objetos con bind1st. –

16

Además de los typedefs (ya mencionado), también hay como aspecto de legibilidad. Cuando veo struct Foo {... mi primer pensamiento será "Foo es un tipo". Pero con struct Foo : public unary_function<... ya sé que Foo es un funtor. Para un programador (a diferencia de los compiladores), los tipos y funtores son bastante distintos.

+6

Para ser estricto, Foo nunca es un funtor. Es un tipo, o, específicamente, un functor * tipo *. Los propios funtores son instanciaciones de tipos de funtores. –

3

Es una buena forma de documentación impuesta por el compilador.

Al heredar, promete que implementará la interfaz binary_function y el compilador lo retendrá. Entonces, los clientes pueden confiar en que su clase se puede usar donde sea que se necesite una función binaria.

Cuestiones relacionadas