C/C++:std::function 和 std::bind 以及占位符 std::placeholders::_n 的使用

头文件:

std::function<...>

       std::function的实例可以对任何能够调用的目标实体进行封装调用,这些目标实体包括普通函数、lambda表达式、函数指针、仿函数、类的普通成员函数和类的静态成员函数等。std::function对象是对C++中现有的可调用实体的一种类型安全的封装(我们知道像函数指针这类可调用实体是类型不安全的),示例代码如下

#include 
#include 
using namespace std;

std::function func;

// 普通函数
int testFunc(int a)
{
    return a;
}

// lambda表达式
auto lambda = [](int a)->int{ return a; };

// 仿函数(实现operator()的函数)
class Functor
{
public:
    int operator()(int a)
    {
        return a;
    }
};

// 类的普通成员函数和静态成员函数
class TestClass
{
public:
    int ClassMember(int a) { return a; }
    static int StaticMember(int a) { return a; }
};

int main()
{
    // 普通函数
    func = testFunc;
    int result = func(10);
    cout << "普通函数:"<< result << endl;

    // lambda表达式
    func = lambda;
    result = func(20);
    cout << "lambda表达式:"<< result << endl;

    // 仿函数
    Functor testFunctor;
    func = testFunctor;
    result = func(30);
    cout << "仿函数:"<< result << endl;

    // 类的普通成员函数
    TestClass testObj;
    func = std::bind(&TestClass::ClassMember, &testObj, std::placeholders::_1);
    result = func(40);
    cout << "类的普通成员函数:"<< result << endl;

    // 类的静态成员函数
    func = TestClass::StaticMember;
    result = func(50);
    cout << "类的静态成员函数:"<< result << endl;

    return 0;
}

std::bind

       std::bind是这样一种机制,它可以预先把指定可调用实体的某些参数绑定到已有的变量,产生一个新的可调用实体。可将std::bind函数看作一个通用的函数适配器,它接受一个可调用对象,生成一个新的可调用对象来“适应”原对象的参数列表。std::bind将可调用对象与其参数一起进行绑定,绑定后的结果可以使用std::function保存。std::bind主要有以下几个作用:

  • 将可调用对象和其参数绑定成一个仿函数
  • 只绑定部分参数,减少可调用对象传入的参数
  • 改变参数绑定顺序

std::bind绑定普通函数

double myDivide(double x, double y) 
{
    return x/y;
}
auto fn_half = std::bind(myDivide, 1, 2);  
std::cout << fn_half() << std::endl;                        // 0.5

       bind的第一个参数是函数名,普通函数做实参时,会隐式转换成函数指针。因此std::bind (myDivide,1,2)等价于std::bind (&myDivide,1,2)

std::bind绑定一个成员函数

struct Foo {
    void print_sum(int n1, int n2)
    {
        std::cout << n1+n2 << '\n';
    }
    int data = 10;
};
int main() 
{
    Foo foo;
    auto f = std::bind(&Foo::print_sum, &foo, 95, 5);
    f(); // 100
}

       bind绑定类成员函数时,第一个参数表示对象的成员函数的指针,第二个参数表示对象的地址。必须显示的指定&Foo::print_sum,因为编译器不会将对象的成员函数隐式转换成函数指针,所以必须在Foo::print_sum前添加&。使用对象成员函数的指针时,必须要知道该指针属于哪个对象,因此第二个参数为对象的地址 &foo;

std::bind绑定一个引用参数

默认情况下,bind的那些不是占位符(占位符的概念后面介绍)的参数被拷贝到bind返回的可调用对象中。但是,与lambda类似,有时对有些绑定的参数希望以引用的方式传递,或是要绑定参数的类型无法拷贝。

#include 
#include 
#include 
#include 
#include 
using namespace std::placeholders;
using namespace std;

ostream & print(ostream &os, const string& s, char c)
{
    os << s << c;
    return os;
}

int main()
{
    vector words{"hello", "world", "this", "is", "C++11"};
    ostringstream os;
    char c = ' ';
    for_each(words.begin(), words.end(), 
                   [&os, c](const string & s){os << s << c;} );
    cout << os.str() << endl;

    ostringstream os1;
    // ostream不能拷贝,若希望传递给bind一个对象,
    // 而不拷贝它,就必须使用标准库提供的ref函数
    for_each(words.begin(), words.end(),
                   bind(print, ref(os1), _1, c));
    cout << os1.str() << endl;
}

std::placeholders::_n

前面的 std::bind 可以绑定一个可调用对象和调用这个可调用对象所需要的参数,比如上面的例子

double myDivide(double x, double y) 
{
    return x/y;
}
auto fn_half = std::bind(myDivide, 1, 2);  
std::cout << fn_half() << std::endl;                        // 0.5

       这段代码中我们把可调用的函数封装在了fn_half中,并把想传入的参数同时传了进去,因此结果是0.5,但如果我们只确定了一个想传入的参数,另一个想在调用std::bind返回的fn_half中使用,那么我们就需要占位符,因此可以概括一下占位符的主要作用可以减少调用参数的数量,也可以将调用参数的位置互换。代码如下

// 减少1个参数
double myDivide(double x, double y) 
{
    return x/y;
}
auto fn_half = std::bind(myDivide, std::placeholders::_1, 2);  
std::cout << fn_half(1) << std::endl;                           // 0.5

// 减少2个参数
double myDivide(double x, double y) 
{
    return x/y;
}
auto fn_half = std::bind(myDivide, 1, 2);  
std::cout << fn_half() << std::endl;                            // 0.5

// 参数位置互换
double myDivide(double x, double y) 
{
    return x/y;
}
auto fn_half = std::bind(myDivide, std::placeholders::_2, std::placeholders::_1);  
std::cout << fn_half(1, 2) << std::endl;                        // 2.0

注意:如果在bind函数中使用了特定数量占位符,那么在调用bind函数返回的可调用对象时,也要有同等数量的参数。

你可能感兴趣的:(C/C++)