В чем преимущество наследования от std:: binary_function (или std:: унарная функция)?
В чем преимущество наследования от std:: binary_function (или std:: unary_function)?
Например, у меня есть такой код:
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;
}
};
ОБНОВЛЕНО
std:: binary_function и std:: unary_function устарели от С++ 11, см. комментарий от @AlexandreC.
Ответы
Ответ 1
Наследование из функции [unary | binary] _ просто дает вам дополнительные typedef в вашем классе:
Для unary_function
argument_type
result_type
Для бинарной_функции
first_argument_type
second_argument_type
result_type
Какими типами вы переходите к [unary | binary] _функции.
В вашем случае нет никаких преимуществ.
Если вы когда-нибудь захотите использовать свои функторы с другими модификаторами-модификаторами std, такими как not1, bind1st, вам нужно наследовать от [unart | binart] _function.
И если вы собираетесь хранить эту информацию о шаблоне для своей цели, лучше использовать готовое решение.
Ответ 2
Помимо typedefs (уже упоминалось), есть также аспект читаемости. Когда я вижу struct Foo {...
, моя первая мысль будет "Foo - это тип". Но с struct Foo : public unary_function<...
я уже знаю, что Foo - функтор. Для программиста (в отличие от компиляторов) типы и функторы довольно различны.
Ответ 3
Как объясняет Микола, они просто добавляют typedefs. Представьте себе, что ваш PersonGreater
, вы хотите исправить первый аргумент для кого-то. binder1st
нужно будет хранить первый аргумент где-то, и поэтому ему нужен тип первого аргумента. binary_function
указывает, что в качестве typedef:
// get a function object that compares person1 against
// another person
std::bind1st(PersonGreater(), person1)
Теперь возвращаемый объект binder1st
знает, что тип аргумента, который он должен хранить, имеет тип Person.
Некоторые объекты функций отрицают результат другого объекта функции. Здесь нам нужен тип аргумента:
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
.
Ответ 4
Это сильная форма документации, выполняемая компилятором.
Наследуя, вы обещаете, что вы будете реализовывать интерфейс binary_function, и компилятор проведет вас к нему. Затем клиенты могут доверять тому, что ваш класс может использоваться везде, где требуется функция binary_function.