2009-03-04 2 views
27

В чем преимущество наследования от std :: binary_function (или std :: unary_function)?В чем преимущество наследования от std :: binary_function (или std :: унарная функция)?

Например, у меня есть такой код:

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

Но я также мог бы написать этот код без формы наследования 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; 
    } 
}; 

ОБНОВЛЕНО

станд :: binary_function и станд :: unary_function являются устаревшими C++ 11 см замечания @AlexandreC.

+2

Они были устаревшими в C++ 11 (разумеется, на момент запроса не было C++ 11). –

+0

@AlexandreC. что мы должны использовать вместо этого? в стандартах кодирования C++, andrei упоминает, что они имеют решающее значение для построения функторов, которые будут использоваться с stl-алгоритмами. –

+0

@kirill_igum: теперь, когда доступны 'decltype',' auto' и 'std :: result_of', вам не нужно наследовать что-нибудь. Кроме того, 'bind1st' и' bind2nd' устарели в пользу 'bind'. –

ответ

29

Наследование из [унарных | бинарной] _function просто дает вам дополнительные определений типов в классе:

Для unary_function

argument_type 
result_type 

Для binary_function

first_argument_type 
second_argument_type 
result_type 

которые являются теми типами вы передаете к [унарной | двоичной] функции. В вашем случае нет никаких преимуществ.

Если вы когда-нибудь захотите использовать ваши функторы с другими модификаторами-модификаторами std, такими как not1, bind1st, вы должны наследовать от [unart | binart] _функции.

И если вы собираетесь хранить эту информацию о шаблоне для своей цели, лучше использовать готовое решение.

+1

Вы должны заметить, что эти typedefs используются библиотекой stl, например, negators not1 и not2. –

+0

@ Luc: Спасибо за уведомление. –

9

Как объясняет Микола, они просто добавляют typedefs. Представьте себе, что ваш PersonGreater, вы хотите исправить первый аргумент для кого-то. binder1st нужно будет хранить первый аргумент где-то, и поэтому ему нужен тип первого аргумента. binary_function предусматривает, что в качестве ЬурейеЕ:

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

Теперь, возвращаемый binder1st объект знает, что тип аргумента он должен хранить это тип лица.

Некоторые объекты функций отрицают результат другого объекта функции. Здесь нам нужно тип аргумента тоже:

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

Это может также использовать шаблонный operator(), но стандарт определяет его использовать argument_type тип в качестве параметра. Сам отрицатель выведен из unary_function и в любом случае должен обеспечить первый тип аргумента.

Иногда люди пытаются использовать [unary,binary]_function для хранения объектов объектов/указателей. Однако они не могут быть использованы для этого. boost::function выполняет эту работу и будет принят в следующем стандарте как std::function.

+0

@litb: Хорошая мысль о bind1st. Никогда не использовались мои объектные функторы с bind1st. –

16

Помимо typedefs (уже упоминалось), есть также аспект читаемости. Когда я вижу struct Foo {..., моя первая мысль будет «Foo - это тип». Но с struct Foo : public unary_function<... Я уже знаю, что Foo является функтором. Для программиста (в отличие от компиляторов) типы и функторы весьма различны.

+6

Чтобы быть строгим, Foo никогда не является функтором. Это либо тип, либо, в частности, тип функтора *. Сами функторы - это экземпляры типов функторов. –

3

Это сильная форма документации, выполняемая компилятором.

По наследству вы обещаете реализовать интерфейс binary_function, и компилятор проведет вас к нему. Затем клиенты могут доверять тому, что ваш класс может использоваться везде, где требуется бинарная функция.