天天看點

C++ STL|Predefined Function Adapters:binder

作者:小智雅彙

A function adapter is a function object that enables the composition of function objects with each other, with certain values, or with special functions.

Expression Effect
bind(op,args...) Binds args to op
mem_fn(op) Calls op() as a member function for an object or pointer to object
not1(op) Unary negation: !op(param)
not2(op) Binary negation: !op(param1,param2)

The most important adapter is bind().

It returns a function object based on fn, but with its arguments bound to args.

Each argument may either be bound to a value or be a placeholder.

It allows you to:

• Adapt and compose new function objects out of existing or predefined function objects.

• Call global functions.

• Call member functions for objects, pointers to objects, and smart pointers to objects.

In general, bind() binds parameters for callable objects. Thus, if a function, member function, function object, or lambda requires some parameters, you can bind them to specific or passed arguments. Specific arguments you simply name. For passed arguments, you can use the predefined placeholders _1, _2, . . . defined in namespace std::placeholders.

#include <functional>
#include <iostream>

int main()
{
    auto plus10 = std::bind(std::plus<int>(), // param1+10
                            std::placeholders::_1,
                            10);
    std::cout << "+10:    " << plus10(7) << std::endl;

    auto plus10times2 = std::bind(std::multiplies<int>(), // (param1+10)*2
                                  std::bind(std::plus<int>(),
                                            std::placeholders::_1,
                                            10),
                                  2);
    std::cout << "+10 *2: " << plus10times2(7) << std::endl;

    auto pow3 = std::bind(std::multiplies<int>(), // (param1*param1)*param1
                          std::bind(std::multiplies<int>(),
                                    std::placeholders::_1,
                                    std::placeholders::_1),
                          std::placeholders::_1);
    std::cout << "x*x*x:  " << pow3(7) << std::endl;

    auto inversDivide = std::bind(std::divides<double>(), // param2/param1
                                  std::placeholders::_2,
                                  std::placeholders::_1);
    std::cout << "invdiv: " << inversDivide(49,7) << std::endl;
}
/*output:
+10: 17
+10 *2: 34
x*x*x: 343
invdiv: 0.142857
*/           

The following program demonstrates how bind() can be used to call member functions:

#include <functional>
#include <algorithm>
#include <vector>
#include <iostream>
#include <string>
using namespace std;
using namespace std::placeholders;

class Person {
  private:
    string name;
  public:
    Person (const string& n) : name(n) {
    }
    void print () const {
        cout << name << endl;
    }
    void print2 (const string& prefix) const {
        cout << prefix << name << endl;
    }
    //...
};

int main()
{
    vector<Person> coll
            = { Person("Tick"), Person("Trick"), Person("Track") };

    // call member function print() for each person
    for_each (coll.begin(), coll.end(),
              bind(&Person::print,_1));
    cout << endl;

    // call member function print2() with additional argument for each person
    for_each (coll.begin(), coll.end(),
              bind(&Person::print2,_1,"Person: "));
    cout << endl;

    // call print2() for temporary Person
    bind(&Person::print2,_1,"This is: ")(Person("nico"));
}
/*output:
Tick
Trick
Track
Person: Tick
Person: Trick
Person: Track
This is: nico
*/           

For member functions, you can also use the mem_fn() adapter, whereby you can skip the placeholder for the object the member function is called for:

std::for_each (coll.begin(), coll.end(),
std::mem_fn(&Person::print));           

Thus, mem_fn() simply calls an initialized member function for a passed argument while additional arguments are passed as parameters to the member function:

std::mem_fn(&Person::print)(n); // calls n.print()
std::mem_fn(&Person::print2)(n,"Person: "); // calls n.print2("Person: ")           

However, to bind an additional argument to the function object, you again have to use bind():

std::for_each (coll.begin(), coll.end(), 
std::bind(std::mem_fn(&Person::print2),
std::placeholders::_1,
"Person: "));           

You can also bind to data members. Consider the following example (namespaces omitted):

map<string,int> coll; // map of int values associated to strings
...
// accumulate all values (member second of the elements)
int sum
= accumulate (coll.begin(), coll.end(),
0,
bind(plus<int>(),
_1,
bind(&map<string,int>::value_type::second,
_2)));           

Here, accumulate() is called, which uses a binary predicate to sum up all values of all elements. However, because we use a map, where the elements are key/value pairs, to gain access to an element’s value

bind(&map<string,int>::value_type::second,_2)           

binds the passed second argument of each call of the predicate to its member second.

another demo:

// bind example
#include <iostream>     // std::cout
#include <functional>   // std::bind

// a function: (also works with function object: std::divides<double> my_divide;)
double my_divide (double x, double y) {return x/y;}

struct MyPair {
  double a,b;
  double multiply() {return a*b;}
};

int main () {
  using namespace std::placeholders;    // adds visibility of _1, _2, _3,...

  // binding functions:
  auto fn_five = std::bind (my_divide,10,2);               // returns 10/2
  std::cout << fn_five() << '\n';                          // 5

  auto fn_half = std::bind (my_divide,_1,2);               // returns x/2
  std::cout << fn_half(10) << '\n';                        // 5

  auto fn_invert = std::bind (my_divide,_2,_1);            // returns y/x
  std::cout << fn_invert(10,2) << '\n';                    // 0.2

  auto fn_rounding = std::bind<int> (my_divide,_1,_2);     // returns int(x/y)
  std::cout << fn_rounding(10,3) << '\n';                  // 3

  MyPair ten_two {10,2};

  // binding members:
  auto bound_member_fn = std::bind (&MyPair::multiply,_1); // returns x.multiply()
  std::cout << bound_member_fn(ten_two) << '\n';           // 20

  auto bound_member_data = std::bind (&MyPair::a,ten_two); // returns ten_two.a
  std::cout << bound_member_data() << '\n';                // 10

  return 0;
}
/*output:
5
5
0.2
3
20
10
*/           

Binders is identically implemented by lambda:

#include <iostream>
int main()
{
    auto plus10 = [] (int i) {
        return i+10;
    };
    std::cout << "+10: " << plus10(7) << std::endl;
    auto plus10times2 = [] (int i) {
        return (i+10)*2;
    };
    std::cout << "+10 *2: " << plus10times2(7) << std::endl;
    auto pow3 = [] (int i) {
        return i*i*i;
    };
    std::cout << "x*x*x: " << pow3(7) << std::endl;
    auto inversDivide = [] (double d1, double d2) {
        return d2/d1;
    };
    std::cout << "invdiv: " << inversDivide(49,7) << std::endl;
}
           

ref:

Nicolai M. Josuttis 《The C++ Standard Library》

http://www.cplusplus.com/reference/functional/bind/

-End-

繼續閱讀