Для чего нужна функция unary_function и binary_function?
Я прочитал учебники о двоичных и унарных функциях. Я понял их структуру, но я не мог себе представить, в каком случае мне нужны эти функции. Можете ли вы привести пример их использования.
http://www.cplusplus.com/reference/std/functional/unary_function/
http://www.cplusplus.com/reference/std/functional/binary_function/
Ответы
Ответ 1
Это не функции, это классы (структуры, фактически, но не имеющие значения). Когда вы определяете свои собственные двоичные функции для использования с алгоритмами STL, вы получаете их из этих классов, чтобы автоматически получить все typedefs.
например.
struct SomeFancyUnaryFunction: public std::unary_function<Arg_t, Result_t>
{
Result_t operator ()(Arg_t const &)
{
...
}
};
теперь вам не нужно вручную предоставлять typedefs для argument_type
, result_type
и т.д. Эти структуры, как и структура iterator
, существуют только для нашего удобства, чтобы повторно использовать typedefs, необходимые для алгоритмов.
Обновление для С++ 11:
Начиная с С++ 11, новый std::bind
не нуждается в каких-либо typedefs, поэтому они в некотором смысле устарели.
Ответ 2
В основном, они обеспечивают все typedef
, необходимые для создания композиции функций более высокого порядка из унарных и двоичных функций с использованием функциональных адаптеров. Например, это позволяет использовать двоичный функтор, где требуется унарный, привязывая один из аргументов к буквальному значению:
std::find_if( begin, end, std::bind1st(greater<int>(),42) );
std::bind1st
полагается на переданный ему функтор для предоставления этих типов.
AFAIK новый std::bind
им не нужен, поэтому в новом коде вы можете использовать std::bind
и покончить с ними.
Ответ 3
Есть объяснение в документации sgi STL Объекты функции. Таким образом, unary_function и binary_function используются для адаптации функторов. Это позволяет использовать их с адаптерами объектных объектов, такими как unary_negate.
Ответ 4
Каковы они?
std:: unary_function и std:: binary_function являются базовыми структурами для создания адаптируемых функциональных объектов. Слово адаптируемое означает, что они предоставляют необходимые typedef для использования в сочетании со стандартными функциональными адаптерами, такими как std:: not1, std:: not2, std:: bind1st, std:: bind2nd.
Когда мне нужно их использовать?
Вы можете использовать их каждый раз, когда вам нужно использовать свой пользовательский объект функции вместе со стандартным функциональным адаптером.
Есть ли у вас пример?
Давайте рассмотрим некоторые примеры (я знаю, они искусственны. С другой стороны, я надеюсь, что они скорее описательны).
Пример 1.
Предположим, вы хотите напечатать все строки в векторе с их длиной не менее определенного порога и напечатать их в std:: cout.
Можно использовать следующий объект функции:
class LengthThreshold
{
public:
LengthThreshold(std::size_t threshold) : threshold(threshold) {}
bool operator()(const std::string& instance) const
{
return (instance.size() < threshold);
}
private:
const std::size_t threshold;
};
Теперь задача довольно проста и может быть выполнена с помощью алгоритма std:: remove_copy_if:
// std::size_t threshold is defined somewhere
std::remove_copy_if(some_strings.begin(), some_strings.end(),
std::ostream_iterator<std::string>(std::cout, "\n"),
LengthThreshold(threshold)
);
Что делать, если вы хотите использовать один и тот же функциональный объект для печати всех строк с их длиной строго меньше порога?
Очевидным решением, которое мы можем придумать, является использование сетевого адаптера std:: not1:
// std::size_t threshold is defined somewhere
std::remove_copy_if(some_strings.begin(), some_strings.end(),
std::ostream_iterator<std::string>(std::cout, "\n"),
std::not1(LengthThreshold(threshold))
);
Фактически, код выше не будет компилироваться, потому что наш LengthThreshold не может быть адаптирован и не имеет typedefs, которые необходимы для std:: not1.
Чтобы сделать его адаптируемым, нам нужно наследовать от std:: unary_function:
class LengthThreshold : public std::unary_function<std::string, bool>
{
// Function object body remains the same
}
Теперь наш первый пример работает как шарм.
Пример 2.
Позволяет изменить наш предыдущий пример. Предположим, мы не хотим хранить порог внутри объекта функции. В этом случае мы можем изменить объект функции из унарного предиката на двоичный предикат:
class LengthThreshold : public std::binary_function<std::string, std::size_t, bool>
{
public:
bool operator()(const std::string& lhs, std::size_t threshold) const
{
return lhs.size() < threshold;
}
};
И используйте сетевой адаптер std:: bind2nd:
// std::size_t threshold is defined somewhere
std::remove_copy_if(some_strings.begin(), some_strings.end(),
std::ostream_iterator<std::string>(std::cout, "\n"),
std::bind2nd(LengthThreshold(), threshold)
);
Как насчет С++ 11 и выше?
Все приведенные выше примеры намеренно используют только С++ 03.
Причина в том, что std:: unary_function и std:: binary_function устарели с С++ 11 и полностью удалены из С++ 17.
Это случилось с появлением более обобщенных и гибких функций, таких как std:: bind, которые наследуют от std:: unary_function и std:: binary_function избыточно.