Function Adapters

Function Adapters

Just as iterator adapters help you construct a wider variety of iterators, a Function Adapter helps you create a wider variety of function objects.

STL provides three kinds of function adapters:

  • Binders : Converts function objects that take two arguments into function objects that take one.
  • Negaters : Create a new function object class that inverts the result of a function.
  • Adapters for pointers to functions : Useful if you already have a function defined, and want to create a function object out of it.

WARNING:

It appears that a number of these function adapters are now deprecated in C++11, and removed in C++17.

There are alternative approaches recommended below, and on the web.

Binders

In the C++ STL, binders are a type of functors that bind or associate some of the arguments of a function to a fixed value. This fixed value is stored inside the functor and the rest of the arguments can be passed dynamically at the time of calling the functor.

Here is an example of using a binder.

The find_if() algorithm is like find(), except that it takes a unary predicate function object (a function object encapsulating a one-argument function that returns a bool value.)

//
//  main.cpp
//  STL Function Adapters
//
//  Created by Bryan Higgs on 10/24/24.
//

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

// Binder example
int main(int argc, const char * argv[])
{
  std::vector<int> values = { 10, -200, 30, -90, 56, 89 };
  
  // Find all elements greater than 20
  std::vector<int>::iterator it =
          std::find_if(values.begin(), values.end(),
                       std::bind2nd(std::greater<int>(), 20));
  if (it != values.end())
  {
    std::cout << "First element greater than 20: " << *it << std::endl;
  }
  
  return 0;
}

… which produces the following output:

First element greater than 20: 30
Program ended with exit code: 0

We constructed the predicate for the search by starting with greater<int>(), a function object that defines a binary function:

bool operator()(int x, int y) const
{ return x > y; }

By applying bind2nd to this function object, and 50, we produced a function object that defines a unary function:

bool operator()(int x) const
{ return x > 50; }

as if we had programmed another function object type:

struct GreaterThan50 : unary_function<int, bool>
{
	bool operator()(int x) const
	{ return x > 50; }
};

The bind2nd binder binds the second argument of the binary operator.  There is also a bind1st binder which may be used to bind the first argument in a similar way

NOTE:

bind1st and bind2nd are deprecated in C++11 and removed in C++17.

The above code was compiled and run using C++11, and produced the following warning:

main.cpp:21:29 'bind2nd<std::greater<int>, int>' is deprecated
bind2nd<std::greater<int>, int>' has been explicitly marked deprecated here

It refused to compile using C++17.

Starting from C++11, the C++ Standard Library provides std::bind, which is a more general binder that can bind any number of arguments to any values and returns a new function object. The new function object can then be used as a regular function, taking the remaining unbound arguments.

Syntax of std::bind in C++

std::bind(function, arg1, arg2, ..., argN);

Parameters in std::bind in C++

  • function is the function or function object that you want to bind some of its arguments.
  • arg1, arg2, …, argN are the arguments to bind. These arguments can be any type of value or expression, including references, pointers, and placeholder values (e.g., std::placeholders::_1, std::placeholders::_2, etc.)

The current advice is to consider using lambdas instead, which are more flexible and readable.” (For lambda descriptions, see later)

Negaters

A Negater is a kind of function adapter that is used to reverse the sense of a predicate function object.

STL provides two negaters:

  • not1 : Negates a unary predicate
  • not2 : Negates a binary predicate

The not1 negater, given a unary predicate function object p, generates a unary predicate object that defines the function:

bool operator()(const T &x) const
{ return !(p(x)); }

The not1 Negater

Here is an example of a not1 negator:

//
//  main.cpp
//  STL Function Adapters
//
//  Created by Bryan Higgs on 10/24/24.
//

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

// not1 Negator example
int main(int argc, const char * argv[])
{
  std::vector<int> values = { 10, -200, 30, -90, 56, 89 };
  
  // Find all elements greater than 20
  std::vector<int>::iterator it =
  std::find_if(values.begin(), values.end(),
               not1(
                    std::bind2nd(std::greater<int>(), 20)
                    )
               );
  if (it != values.end())
  {
    std::cout << "First element not greater than 20: " << *it << std::endl;
  }
  
  return 0;
}

… which produces:

First element not greater than 20: 10
Program ended with exit code: 0

This code was compiled using C++11, with the bind2nd deprecation warning.

It will not compile using C++17

The not2 Negater

Assume we have a class, CompareIDs, of objects with a binary predicate function object that compares objects based on an id field.

//
//  CompareIDs.h
//  STL Function Adapters
//
//  Created by Bryan Higgs on 10/24/24.
//

#ifndef CompareIDs_h
#define CompareIDs_h

#include <iostream>   // for ostream, etc.
#include <functional> // for binary_function

class CompareIDs
  : public std::binary_function<CompareIDs, CompareIDs, bool>
{
public:
  int id;  // ID field
  bool operator()(const CompareIDs &x, const CompareIDs &y) const
  {
    return x.id >= y.id;
  }
  
  friend std::ostream &operator<<(std::ostream &o, const CompareIDs &x)
  {
    o << x.id;
    return o;
  }
};

#endif /* CompareIDs_h */

Assume we want to sort a vector of CompareIDs objects

We can’t use default ordering, since operator < is not defined for CompareIDs.

We can’t use the overloaded operator defined in the type, because it uses >= to compare ids, not <

We can, however, use a not2 negator to solve the problem:

//
//  main.cpp
//  STL Function Adapters
//
//  Created by Bryan Higgs on 10/24/24.
//

#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>

#include "compareIDs.h"

static const size_t SIZE = 15;

void display(std::vector<CompareIDs> v)
{
  for (int i = 0; i < SIZE; i++)
  {
    std::cout << v[i] << std::endl;
  }
}

int main(int argc, const char * argv[])
{
  std::vector<CompareIDs> v(SIZE);
  
  for (int i = 0; i < SIZE; i++)
  {
    v[i].id = SIZE - (i + 1);
  }
  
  std::cout << "Before sorting:" << std::endl;
  display(v);
  std::cout << std::endl;
  
  sort( v.begin(), v.end(), not2(CompareIDs()) );
  
  std::cout << "After sorting:" << std::endl;
  display(v);
  std::cout << std::endl;
  
  return 0;
}

… which produces the following output:

Before sorting:
14
13
12
11
10
9
8
7
6
5
4
3
2
1
0

After sorting:
0
1
2
3
4
5
6
7
8
9
10
11
12
13
14

Program ended with exit code: 0

NOTE:

The above program was compiled using C++11, and it received the following warning:

CompareIDs.h:15:17 'binary_function<CompareIDs, CompareIDs, bool>' is deprecated

It refused to compile with C++17.

So, binary_function is deprecated in C++11, and removed in C++17.

Why was it deprecated?

  • Redundancy with C++11 features: With the introduction of features like decltype and trailing return types in C++11, it became simpler to define the necessary types (first_argument_typesecond_argument_typeresult_type) directly within your function object class without the need for a base class.

How to replace it?

  • Define the types directly: If you need to define a binary function object, simply define the required typedefs within your class:

Adapters for Pointers to Functions

Adapters for Pointers to Functions are provided to allow pointers to unary and binary functions to work with the other function adapters.

Suppose we have two different sets that differ only in their comparison functions:

set<string, less<string> > set1;
set<string, greater<string> > set2;

This can produce problems with the compiler generating multiple copies of much of the code needed to support both of these sets.

Instead, we can use a single instance of set, by using an adapter for pointers to functions as the type of comparison function to be used to determine the set order:

set< string, pointer_to_binary_function<const string &, const string &, bool> >

Here’s an example of how you could use this functionality:

//
//  main.cpp
//  STL Function Adapters
//
//  Created by Bryan Higgs on 10/24/24.
//

#include <iostream>
#include <string>
#include <set>

// Use a typedef to simplify types
typedef std::set<std::string,
                std::pointer_to_binary_function
                    <const std::string &,
                     const std::string &,
                     bool> >   SET_TYPE;

bool myLess(const std::string &x, const std::string &y)
{
  return x < y;
}

bool myGreater(const std::string &x, const std::string &y)
{
  return x > y;
}

void populateSet(SET_TYPE &s)
{
  s.insert("Have");
  s.insert("you");
  s.insert("hugged");
  s.insert("your");
  s.insert("sweetie");
  s.insert("today");
  s.insert("?");
}

int main(int argc, const char * argv[])
{
  SET_TYPE set1( ptr_fun(myLess) );
  populateSet(set1);
  
  std::cout << "set1 (myLess):" << std::endl;
  SET_TYPE::iterator i;
  for (i = set1.begin(); i != set1.end(); i++)
  {
    std::cout << *i << std::endl;
  }
  std::cout << std::endl;

  std::cout << "set2 (myGreater):" << std::endl;
  SET_TYPE set2( ptr_fun(myGreater) );
  populateSet(set2);
  for (i = set2.begin(); i != set2.end(); i++)
  {
    std::cout << *i << std::endl;
  }
  std::cout << std::endl;
  
  return 0;
}

… which outputs:

set1 (myLess):
?
Have
hugged
sweetie
today
you
your

set2 (myGreater):
your
you
today
sweetie
hugged
Have
?

Program ended with exit code: 0

NOTE

The above program was compiled with C++11, and received the following warnings:

main.cpp:14:22 'pointer_to_binary_function<const std::string &, const std::string &, bool>' is deprecated

main.cpp:42:18 'ptr_fun<const std::string &, const std::string &, bool>' is deprecated

main.cpp:54:18 'ptr_fun<const std::string &, const std::string &, bool>' is deprecated

It failed to compile using C++17.

So, pointer_to_binary_function and ptr_fun are deprecated in C++11, and removed in C++17.

“You should use modern alternatives like lambdas or std::function instead.”

Categories of Generic Algorithms

The STL categorizes its algorithms as follows:

Nonmutating Sequence Algorithms

for_each, find, find_end, find_first_of, adjacent_find, count, mismatch, equal, search, search_n

Mutating Sequence Algorithms

copy, swap, transform, replace, fill, generate, remove, unique, reverse, rotate, random_shuffle, partition

Sorting and Related Operations

sort, stable_sort, partial_sort, nth_element, binary_search, lower_bound, upper_bound, equal_range, merge, includes, set_union, set_intersection, set_difference, set_symmetric_difference, push_heap, pop_heap, make_heap, sort_heap, min, max, min_element, max_element, lexicographic_compare, next_permutation, prev_permutation

Generalized Numeric Operations

accumulate, inner_product, partial_sum, adjacent_difference

Index