【C++】std::bind与functional函数对象

发布时间:2023年12月17日

functional

std::bind

原型:

template< class R, class F, class... Args >
constexpr /* 未指定 */ bind( F&& f, Args&&... args );

函数模板 std::bind 生成 f 的转发调用包装器。调用此包装器等价于以一些绑定到 args 的参数调用 f

  • f — 可调用 (Callable) 对象(函数对象、指向函数指针、到函数引用、指向成员函数指针或指向数据成员指针)
  • args ---- 要绑定的参数列表,未绑定参数会被命名空间 std::placeholders 的占位符 _1,_2, _3… 替换

使用示例

#include <functional>
#include <iostream>
#include <memory>
#include <random>
 
void f(int n1, int n2, int n3, const int& n4, int n5)
{
    std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << '\n';
}
 
int g(int n1)
{
    return n1;
}
 
struct Foo
{
    void print_sum(int n1, int n2)
    {
        std::cout << n1 + n2 << '\n';
    }
 
    int data = 10;
};
 
int main()
{
    using namespace std::placeholders;  // 对于 _1, _2, _3...
 
    std::cout << "参数重排序和按引用传递:";
    int n = 7;
    // ( _1 与 _2 来自 std::placeholders ,并表示将来会传递给 f1 的参数)
    //std::cref返回需要被包装的到对象的左值引用,返回后面n的值
    auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);
    n = 10;
    f1(1, 2, 1001); // 1 为 _1 所绑定, 2 为 _2 所绑定,不使用 1001
                    // 进行到 f(2, 42, 1, n, 7) 的调用
 
    std::cout << "嵌套 bind 子表达式共享占位符:";
    auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);
    f2(10, 11, 12); // 进行到 f(12, g(12), 12, 4, 5); 的调用
 
    std::cout << "绑定指向成员函数指针:";
    Foo foo;
    auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);
    f3(5);
 
    std::cout << "绑定是指向成员函数指针的 mem_fn:";
    auto ptr_to_print_sum = std::mem_fn(&Foo::print_sum);
    auto f4 = std::bind(ptr_to_print_sum, &foo, 95, _1);
    f4(5);
 
    std::cout << "绑定指向数据成员指针:";
    auto f5 = std::bind(&Foo::data, _1);
    std::cout << f5(foo) << '\n';
 
    std::cout << "绑定是指向数据成员指针的 mem_fn:";
    auto ptr_to_data = std::mem_fn(&Foo::data);
    auto f6 = std::bind(ptr_to_data, _1);
    std::cout << f6(foo) << '\n';
 
    std::cout << "使用智能指针调用被引用对象的成员:";
    std::cout << f6(std::make_shared<Foo>(foo)) << '\n'
              << f6(std::make_unique<Foo>(foo)) << '\n';
}

输出:

参数重排序和按引用传递:2 42 1 10 7
嵌套 bind 子表达式共享占位符:12 12 12 4 5
绑定指向成员函数指针:100
绑定是指向成员函数指针的 mem_fn:100
绑定指向数据成员指针:10
绑定是指向数据成员指针的 mem_fn:10
使用智能指针调用被引用对象的成员:10
10

std::function

类模板 std::function 是通用多态函数包装器。 std::function 的实例能存储、复制及调用任何可复制构造的可调用目标——函数(通过其指针)、 lambda 表达式、 bind 表达式或其他函数对象,还有指向成员函数指针和指向数据成员指针。

存储的可调用对象被称为 std::function 的目标。若 std::function 不含目标,则称它为空。调用空 std::function 的目标导致抛出 std::bad_function_call 异常。

  • 实际上时对函数指针的优化
typedef  void*ptr)(intint// 这里的ptr就是一个函数指针
            |
            |
            |
            V
std::function<void(int ,int)> func;
  • 头文件
#include <functional>

类模板

template< class R, class... Args >
class function<R(Args...)>;
  • R: 被调用函数的返回类型
  • Args…:被调用函数的形参

成员函数

  • 构造函数

从各种资源构造 std::function

  • 析构函数

销毁 std::function 对象。若 std::function 非空,则亦销毁其目标。

  • operator=

赋值新目标给 std::function 。

  1. 赋值 other 的目标副本,如同以执行 function(other).swap(*this);
  2. 移动 other 的目标到 *this 。 other 在有未指定值的合法状态。
  3. 舍弃当前目标。 *this 在调用后为空。
template< class F >
function& operator=( std::reference_wrapper<F> f ) noexcept;
  • swap

交换 *this 与 other 存储的可调用对象。

  • operator bool

检查是否包含了有效的目标

  • operator()

调用其目标

  • target_type

返回存储的函数的类型。

  • target

返回指向存储的可调用函数目标的指针。

使用

#include <functional>
#include <iostream>
 
struct Foo {
    Foo(int num) : num_(num) {}
    void print_add(int i) const { std::cout << num_+i << '\n'; }
    int num_;
};
 
void print_num(int i)
{
    std::cout << i << '\n';
}
 
struct PrintNum {
    void operator()(int i) const
    {
        std::cout << i << '\n';
    }
};
 
int main()
{
    // 存储自由函数
    std::function<void(int)> f_display = print_num;
    f_display(-9);//-9
 
    // 存储 lambda
    std::function<void()> f_display_42 = []() { print_num(42); };
    f_display_42();//42
 
    // 存储到 std::bind 调用的结果
    std::function<void()> f_display_31337 = std::bind(print_num, 31337);
    f_display_31337();//31337
 
    // 存储到成员函数的调用
    std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;
    const Foo foo(314159);//创建FOO对象
    f_add_display(foo, 1);//314160
    f_add_display(314159, 1);//314160
 
    // 存储到数据成员访问器的调用
    std::function<int(Foo const&)> f_num = &Foo::num_;
    std::cout << "num_: " << f_num(foo) << '\n'; //num_: 314159
 
    // 存储到成员函数及对象的调用
    using std::placeholders::_1;
    std::function<void(int)> f_add_display2 = std::bind( &Foo::print_add, foo, _1 );
    f_add_display2(2);//314161
 
    // 存储到成员函数和对象指针的调用
    std::function<void(int)> f_add_display3 = std::bind( &Foo::print_add, &foo, _1 );
    f_add_display3(3);//314162
 
    // 存储到函数对象(仿函数)的调用
    std::function<void(int)> f_display_obj = PrintNum();
    f_display_obj(18);//18
 
    //递归使用
    std::function<int(int)> factorial = [&](int i) -> int{
        if(i == 1)return 1;
        return i * factorial(i - 1);
    };

    for (int i{5}; i != 8; ++i) { std::cout << i << "! = " << factorial(i) << ";  "; }
}

其他:

#include <functional>
#include <iostream>
#include <string>

int f(int, int) { return 1; }
int g(int, int) { return 2; }


void test(std::function<int(int, int)> const& arg)
{
    int (*const* ptr)(int, int) = arg.target<int(*)(int, int)>();
    if (ptr && *ptr == f)
        std::cout << "it is the function f\n";
    if (ptr && *ptr == g)
        std::cout << "it is the function g\n";
}
 
int main()
{
    test(std::function<int(int, int)>(f));
    test(std::function<int(int, int)>(g));
    std::function<int(int,int)> fn1(f),
                            fn2([](int a , int b) {return a+b;});
    std::cout << fn1.target_type().name() << '\n'
              << fn2.target_type().name() << '\n';
}

输出结果:

it is the function f
it is the function g
PFiiiE
Z4mainEUliiE_

推导指引(C++17 起)

#include <functional>
int func(double) { return 0; }
int main() {
  std::function f{func}; // 指引 #1 推导 function<int(double)>
  int i = 5;
  std::function g = [&](double) { return i; }; // 指引 #2 推导 function<int(double)>
}

文章来源:https://blog.csdn.net/qq_60755751/article/details/135036472
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。