📖
Go C++
  • Introduction
  • Chapter 1: What You Must Know First
    • Virtual Address Space of Process: Memory Partition and Layout
    • Function Call: Stack Frame
    • Program Compiling and Linking
  • Chapter 2: C++ Basics Improvement
    • Default Parameters
    • Inline Function
    • Function Overloading
    • new and delete
    • const and Pointers
    • References in Detail
  • Chapter 3: Object-Oriented Principles
  • Class and Object
  • Constructor and Destructor
  • Shallow Copy and Deep Copy
  • Initializer List
  • Various Member Functions
  • Pointer to Class Members
  • Chapter 4: Template Programming
  • Function Templates
  • Class Templates
  • Memory Allocators
  • Chapter 5: Operator Overloading
    • Operator Overloading
    • Introduction to Iterators
    • Issues of Iterator Invalidation
    • More about new and delete
    • Overloading of new and delete: Object Pool
  • Chapter 6: Inheritance and Polymorphism
    • Look inside Inheritance
    • More about Inheritance
    • Virtual Functions, Static Binding and Dynamic Binding
    • More about Virtual Functions
    • Understanding Polymorphism
    • Abstract Classes
    • Frequently Asked Interview Questions: Polymorphism
  • Chapter 7: Multiple Inheritance
    • Virtual Inheritance and Virtual Base Classes
    • Diamond Problem
    • Four Kinds of Type Conversions
  • Chapter 8: Standard Template Library
    • Sequence Containers
    • Container Adaptors
    • Associative Containers
    • More about Iterators
    • Function Objects
    • Generic Algorithms, Binders and Lambda Expressions
  • Chapter 9: Object Optimization
    • Behind the Object
    • Optimizing Objects in Functions
    • Member Functions with Rvalue References
    • Move Semantics and Perfect Forwarding
  • Chapter 10: Smart Pointers
    • Smart Pointers
    • Smart Pointers without Reference Counting
    • Smart Pointers with Reference Counting
    • Custom Deleters
  • Chapter 11: Function Objects and Binders
    • More about Binders
    • Introduction to std::function
    • Template Specialization and Argument Deduction
    • More about std::function
    • std::bind(): A Simple Thread Pool
    • More about Lambda Expressions
  • Chapter 12: Multithreading
    • Important Features in C++11
    • Multithreaded Programming with std::thread
    • Mutual Exclusion
    • Producer-Consumer Problem
    • Atomic Operations
    • Thread Visibility and volatile
  • Chapter 13: Design Patterns
    • Singleton Pattern
    • Factory Pattern
    • Proxy Pattern
    • Decorator Pattern
    • Adapter Pattern
    • Observer Pattern
Powered by GitBook
On this page

Was this helpful?

  1. Chapter 11: Function Objects and Binders

More about Binders

In previous chapter we briefly introduced binders and how they can be used in generic algorithms. With binders, we can convert a binary function object into a unitary function object. Among them, bind1st() binds the first parameter of operator() into a fixed value, while bind2st() binds the second parameter into a fixed value.

The following example sorts an array from largest to smallest. Then we want to insert 48 inside the array and keep the array sorted. bind1st() is used here to bind greater with 48 in order to find the first element smaller than 48.

#include <functional>
int main() {
    int arr[] = {12, 4, 78, 9, 21, 43, 56, 52, 42, 31};
    vector<int> vec(arr, arr+sizeof(arr)/sizeof(arr[0]));
    sort(vec.begin(), vec.end(), greater<int>());
    for (int v : vec) {
        cout << v << " ";   // 78 56 52 43 42 31 21 12 9 4
    }
    auto it = find_if(vec.begin(), vec.end(), bind1st(greater<int>(), 48));
    vec.insert(it, 48);
    for (int v : vec) {
        cout << v << " ";   // 78 56 52 48 43 42 31 21 12 9 4
    }
    ...
    return 0;
}

What is the underlying principle of a binder? Here we try to implement bind1st() ourselves. First we write our own my_find_if() function, which takes two iterators and a function object for the criterion. Then in the function, we call operator() of the function object on every elements within the range until we find the proper one. Apparently, the function object here should be unitary since it only takes one parameter.

template <typename Iterator, typename Compare>
Iterator my_find_if(Iterator first, Iterator last, Compare comp) {
    for (; first != last; ++first) {
        if (comp(*first)) {
            return first;
        }
    }
    return last;
}

Now let's implement mybind1st(). In general, this function takes a binary function object and returns a unitary one. Let's name the returned unitary function object as _mybind1st. Here we don't implement it directly, but encapsulate once with template. This enables the type deduction of template function so that we don't need to pass in template parameters explicitly.

template <typename Compare, typename T>
_mybind1st<Compare, T> mybind1st(Compare comp, const T &val) {
    return _mybind1st<Compare, T>(comp, val);
}

Next is the _mybind1st class, which takes a binary function object and a value to be fixed as member variables. Then the operator() takes one parameter, and simple returns the binary function object with the first parameter fixed to the value.

template <typename Compare, typename T>
class _mybind1st {
public:
    _mybind1st(Compare comp, T val) : _comp(comp), _val(val) {}
    bool operator()(const T &second) {
        return _comp(_val, second);
    }
private:
    Compare _comp;
    T _val;
}

As we can see, the underlying implementation of bind1st() and bind2nd() is simply a multi-layer encapsulation of function objects. However, their limitation is obvious: they are only for binary objects. What if the function object is more complicated, for example with multiple parameters? Fortunately, C++ 11 introduces two classes std::function and std::bind, which is more convenient and powerful.

PreviousCustom DeletersNextIntroduction to std::function

Last updated 4 years ago

Was this helpful?