penguru
penguru

Reputation: 4370

What do we need unary_function and binary_function for?

I read the tutorials about the binary and unary functions. I understood the structure of them, but I couldn't imagine in which case I need these functions. Can you give an example for usage of them.

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

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

Upvotes: 12

Views: 8212

Answers (4)

Edgar Rokjān
Edgar Rokjān

Reputation: 17483

What are they?

std::unary_function and std::binary_function are base structs for creation adaptable function objects. The word adaptable means that they provide necessary typedefs for being used in conjunction with standard function adaptors like std::not1, std::not2, std::bind1st, std::bind2nd.

When I need to use them?

You may use them every time you need to use your custom function object together with standard function adaptor.

Do you have an example?

Lets consider some examples (I know, they are artificial. From the other side I hope, that they are rather descriptive).

Example 1.

Suppose you want to print all strings in a vector with their lengths not less than a particular threshold and print them to std::cout.

One might use the next function object:

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

Now the task is pretty simple and can be performed by std::remove_copy_if algorithm:

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

What if you want to use the same function object to print all the strings with their lengths strictly less than the threshold?

The obvious solution we can come up with is the usage of std::not1 function adaptor:

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

In fact, the code above won't compile because our LengthThreshold is not adaptable and has no typedefs which are necessary for std::not1.

To make it adaptable we need to inherit from std::unary_function:

class LengthThreshold : public std::unary_function<std::string, bool> 
{
    // Function object's body remains the same
}

Now our first example works like a charm.

Example 2.

Lets change our previous example. Suppose we don't want to store a threshold inside the function object. In such case we may change the function object from unary predicate to binary predicate:

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

And make use of std::bind2nd function adaptor:

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

What about C++11 and higher?

All the examples above intentionally use only C++ 03.

The reason is that std::unary_function and std::binary_function are deprecated since C++ 11 and completely removed from C++ 17.

It happened with the advent of more generalized and flexible functions like std::bind which make inheriting from std::unary_function and std::binary_function superfluous.

Upvotes: 4

sbi
sbi

Reputation: 224149

Basically, they provide all the typedefs necessary to allow composition of higher-order functions from unary and binary function objects using function adaptors. For example, this allows using a binary functor where a unary is needed, binding one of the arguments to a literal value:

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

std::bind1st relies on the functor passed to it to provide those types.

AFAIK the new std::bind doesn't need them, so it seems in new code you can use std::bindand do away with them.

Upvotes: 7

Armen Tsirunyan
Armen Tsirunyan

Reputation: 133082

These aren't functions, these are classes (structs, actually, but doesn't matter). When you define your own binary functions to use with STL algorithms, you derive them from these classes in order to automatically get all the typedefs.

E.g.

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

now you don't need to manually provide the typedefs for argument_type, result_type etc. These structs, just like the iterator struct are there just for our convenience, in order to reuse the typedefs needed for algorithms.

Update for C++11:

As of C++11, the new std::bind does not really need any typedefs, so there are, in a way, obsolete.

Upvotes: 13

Daniel Lidstr&#246;m
Daniel Lidstr&#246;m

Reputation: 10280

There's an explanation on the sgi STL documentation of Function Objects. In summary, unary_function and binary_function are used to make functors adaptable. This allows them to be used with function object adaptors such as unary_negate.

Upvotes: 6

Related Questions