Для чего нам нужны unary_function и binary_function? - PullRequest
12 голосов
/ 17 ноября 2010

Я читаю учебники о бинарных и унарных функциях.Я понял их структуру, но не мог представить, в каких случаях мне нужны эти функции.Можете ли вы привести пример их использования?

http://www.cplusplus.com/reference/std/functional/unary_function/

http://www.cplusplus.com/reference/std/functional/binary_function/

Ответы [ 4 ]

13 голосов
/ 17 ноября 2010

Это не функции, это классы (на самом деле, но не имеет значения).Когда вы определяете свои собственные двоичные функции для использования с алгоритмами STL, вы выводите их из этих классов, чтобы автоматически получать все определения типов.

Например,

struct SomeFancyUnaryFunction: public std::unary_function<Arg_t, Result_t>
{
   Result_t operator ()(Arg_t const &)
   {
      ...
   }
};

Теперь вам не нужночтобы вручную предоставить typedefs для argument_type, result_type и т. д. Эти структуры, как и структура iterator, существуют только для нашего удобства, чтобы повторно использовать typedefs, необходимые для алгоритмов.

Обновление для C ++ 11:

Начиная с C ++ 11, новый std::bind на самом деле не нуждается ни в каких typedef, так что они, в некотором смысле, устарели.

7 голосов
/ 17 ноября 2010

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

std::find_if( begin, end, std::bind1st(greater<int>(),42) );

std::bind1st полагается на переданный ему функтор для предоставления этих типов.

AFAIK новым std::bind они не нужны, поэтому в новом коде вы можете использовать std::bind и покончить с ними.

6 голосов
/ 17 ноября 2010

В документации sgi STL Объекты функций есть объяснение.Таким образом, unary_function и binary_function используются для создания функторов adaptable .Это позволяет использовать их с адаптерами объектов функций, такими как unary_negate .

3 голосов
/ 16 ноября 2016

Какие они?

std :: unary_function и std :: binary_function являются базовыми структурами для создания адаптируемых объектов функций. Слово adaptable означает, что они предоставляют необходимые определения типов для использования вместе со стандартными функциональными адаптерами, такими как 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's 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)
);

А как насчет C ++ 11 и выше?

Все примеры, приведенные выше, намеренно используют только C ++ 03 .

Причина в том, что std :: unary_function и std :: binary_function устарели с C ++ 11 и полностью удалены из C ++ 17 .

Это произошло с появлением более обобщенных и гибких функций, таких как std :: bind , которые наследуют от std :: unary_function и std :: binary_function излишний.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...