C++ 11新特性之function

这篇具有很好参考价值的文章主要介绍了C++ 11新特性之function。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

概述

        C++ 11标准库引入了许多创新功能,其中之一便是std::function。作为函数对象容器,std::function允许开发者在编译时不知道具体类型的情况下,存储和传递任意可调用对象,极大地提升了代码的灵活性和可复用性。本文将详细探讨std::function的工作原理、使用场景及其在现代C++编程中的重要地位。

        std::function是C++ 11中<functional>头文件中定义的一个类模板,它能够存储并调用任何具有匹配签名的可调用对象,包括:普通函数、成员函数、Lambda表达式、仿函数等,而且可以给函数添加状态。

function的构成

        function是一组函数对象包装类的模板,实现了一个泛型的回调机制。function与函数指针比较相似,优点在于它允许用户在目标的实现上拥有更大的弹性。

        声明一个function时,需要给出所包装的函数对象的返回值类型和各个参数的类型。其基本声明形式如下:

          std::function<返回类型(参数列表)>

        比如:声明一个function,它返回一个bool类型并接受一个int类型和一个float类型的参数,可以像下面这样:

          function<bool(int, float)> f;

function的接口

        1、function();

        缺省构造函数,创建一个空的函数对象。如果一个空的function被调用,将会抛出一个类型为bad_function_call的异常。

        2、template <typename F> function(F g);

        这个泛型的构造函数接受一个兼容的函数对象,即这样一个函数或函数对象,它的返回类型与被构造的function的返回类型或者一样,或者可以隐式转换,并且它的参数也要与被构造的function的参数类型或者一样,或者可以隐式转换。注意,也可以使用另外一个function实例来进行构造。如果这样做,并且function g为空,则被构造的function也为空。使用空的函数指针和空的成员函数指针也会产生空的function。

        3、template <typename F> function(reference_wrapper<F> g);

        这个构造函数与前一个类似,但它接受的函数对象包装在一个reference_wrapper中,用以避免通过值来传递而产生函数或函数对象的一份拷贝。这同样要求函数对象兼容于function的签名。

        4、function& operator=(const function& g);

        赋值操作符保存g中的函数或函数对象的一份拷贝;如果g为空,被赋值的函数也将为空。

        5、template<typename F> function& operator=(F g);

        这个泛型赋值操作符接受一个兼容的函数指针或函数对象。注意,也可以用另一个 function 实例(带有不同但兼容的签名)来赋值。这同样意味着,如果g是另一个function实例且为空,则赋值后的函数也为空。赋值一个空的函数指针或空的成员函数指针也会使function为空。

        6、bool empty() const;

        这个成员函数返回一个布尔值,表示该function是否含有一个函数或函数对象。如果有一个目标函数或函数对象可被调用,它返回false 。因为一个function可以在一个布尔上下文中测试,或者与0进行比较,因此这个成员函数可能会在未来版本的库中被取消,你应该避免使用它。

        7、void clear();

        这个成员函数清除 function, 即它不再关联到一个函数或函数对象。如果function已经是空的,这个调用没有影响。在调用后,function肯定为空。令一个function为空的首选方法是赋0给它;clear可能在未来版本的库中被取消。

        8、result_type operator()(Arg1 a1, Arg2 a2, ..., ArgN aN) const;

        调用操作符是调用function的方法。你不能调用一个空的 function ,那样会抛出一个bad_function_call的异常。调用操作符的执行会调用function中的函数或函数对象,并返回它的结果。

绑定与赋值

        std::function可以接受各种类型的可调用对象,并通过调用其operator=进行赋值。比如:我们可以将一个Lambda表达式或全局函数绑定到std::function实例。

addFunction = [](int a, int b) { return a + b; };
// 或者是一个全局函数
int add(int a, int b) { return a + b; }
addFunction = &add;

        std::function的使用场景主要包括如下几处。

        事件处理:在设计事件驱动程序时,可以使用std::function来表示事件处理器,方便用户自定义处理逻辑。

        策略模式:在实现策略模式时,不同的算法策略可以通过std::function封装,使得在运行时动态切换策略变得简单。

        回调函数:许多异步操作需要提供一个完成后的回调函数,此时std::function可以作为统一的回调接口。

        泛型编程:编写通用组件时,如果需要接受一个可调用对象作为参数,std::function可以帮助我们写出更加灵活的API。

function的使用

        下面分别给出使用function来包装普通函数,函数对象和类的成员函数的示例代码。

        1、普通函数

int Add(int x, int y)
{
     return x+y;
}

function<int (int,int)> f = Add;
int z = f(2, 3);

        2、函数对象

#include <iostream>
#include <functional>
using namespace std;

class CStudent
{
public:
    void operator() (string strName, int nAge)
    {
        cout << strName << " : " << nAge << endl; 
    }
};

int main()
{
    CStudent stu;
    function<void (string, int)> f = stu;
    f("Mike",  12);
    return 0;
}

        3、类的成员函数

struct TAdd
{
    int Add(int x,int y)
    {
        return x + y;
    }
};

function<int (TAdd *, int, int)> f = TAdd::Add;
TAdd tAdd;
// 如果前面的模板参数为传值或引用,直接传入tAdd即可
f(&tAdd, 2, 3);

        接下来,我们看看使用function来保存函数对象状态的情况。考虑下面的示例代码:

class CAdd
{
public:
    CAdd():m_nSum(0) { NULL; }
    
    int operator()(int i)
    {
        m_nSum += i;
        return m_nSum;
    }
    
    int Sum() const 
    {
        return m_nSum;
    }

private:
    int m_nSum;
};

int main() 
{
    CAdd add;
    function<int (int)> f1 = add;
    function<int (int)> f2 = add;
    cout << f1(10) << "," << f2(10) << "," << add.Sum() << endl;
    return 0;
}

        可能与大家想象的结果不一样,上面程序的输出是:10,10,0。我们将同一个函数对象赋值给了两个function,然后分别调用了这两个function,但函数对象中m_nSum的状态并没有被保持,问题出在哪儿呢?这是因为function的缺省行为是拷贝一份传递给它的函数对象,于是f1和f2中保存的都是add对象的拷贝,调用f1和f2后,add对象中的值并没有被修改。

        C++ 11中提供了ref和cref函数,来提供对象的引用和常引用的包装。要使function能够正确地保存函数对象的状态,我们可以这样来修改上面的示例代码。

CAdd add;
function<int(int)> f1 = ref(add);
function<int(int)> f2 = ref(add);

        另外,在两个function之间赋值时,如果源function保存的是函数对象的拷贝,则目标function保存的也是函数对象的拷贝。如果源function保存的是函数对象的引用,则目标function保存的也是函数对象的引用。

总结

        std::function虽然提供了极大的便利性和灵活性,但需要注意的是,它内部会封装一个类型擦除机制,这可能会带来额外的内存开销和间接调用的成本。因此,在对性能要求极高的场合,直接使用原始的可调用对象或者定制特定的函数指针类型可能更为高效。

        std::function作为C++ 11中的一个重要工具,为程序员提供了强大的可调用对象封装能力,简化了代码设计,提高了代码的可读性和可维护性。然而,任何技术选择都需要权衡利弊,在追求灵活性的同时,适时地评估性能影响,以确保在实际项目中做出最优决策。文章来源地址https://www.toymoban.com/news/detail-801548.html

到了这里,关于C++ 11新特性之function的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • C++ 11新特性之并发

    概述         随着计算机硬件的发展,多核处理器已经成为主流,对程序并发执行能力的需求日益增长。C++ 11标准引入了一套全面且强大的并发编程支持库,为开发者提供了一个安全、高效地利用多核CPU资源进行并行计算的新框架,极大地简化了多线程开发。 std::thread  

    2024年02月20日
    浏览(24)
  • C++ 11 新特性 关键字

    我们先来看一段代码: 在这案例中,我们可以看到,声明为迭代器的变量前缀非常长,这样来写代码非常不美观,也不方便。auto的出现很大程度是为了解决该问题。 现在我们来回答刚才的问题: 在C++中, auto 是一个,它的作用是让编译器自动推导变量的类型,根据变

    2024年02月11日
    浏览(32)
  • C++ 11新特性之bind

    概述         std::bind是C++ 11中functional头文件提供的一个函数模板,它允许我们将函数或成员函数与其部分参数预先绑定在一起,形成一个新的可调用对象(英文为:Callable Object)。这个新的可调用对象可以在后续时机以剩余参数完成调用,这个机制对于事件处理、回调函

    2024年02月21日
    浏览(32)
  • 【C++】—— 简述C++11新特性

    序言: 从本期开始,我将会带大家学习的是关于C++11 新增的相关知识!废话不多说,我们直接开始今天的学习。 目录 (一)C++11简介 (二)统一的列表初始化 1、{}初始化 2、std::initializer_list (三)声明 1、auto 2、decltype 3、nullptr (四)范围for循环 (五)STL中一些变化 总

    2024年02月10日
    浏览(22)
  • 【C++】c++11新特性(一)

    目录 { }列表初始化 内置类型---对单值变量及数组的初始化 列表初始化时进行的类型转换 自定义类型---对类对象或结构的初始化 initializer_list 1. 定义接受 initializer_list 参数的构造函数 2. 在函数中使用 initializer_list 参数 3. 使用 initializer_list 与 vector 等容器 用于推断类型的关键

    2024年04月29日
    浏览(23)
  • 【C++】C++11新特性(下)

      上篇文章(C++11的新特性(上))我们讲述了C++11中的部分重要特性。本篇接着上篇文章进行讲解。本篇文章主要进行讲解: 完美转发、新类的功能、可变参数模板、lambda 表达式、包装器 。希望本篇文章会对你有所帮助。 文章目录 一、完美转发 1、1 实例详解  1、2 应用场

    2024年02月10日
    浏览(22)
  • 【C++】C++11常用新特性

    ✍ 作者 : 阿润菜菜 📖 专栏 : C++ C++11增加的语法特性非常篇幅非常多,我们这里没办法一 一讲解,所以本节主要讲解实际中比较实用的语法。 在C++11中,我们可以使用 {} 列表初始化所有的自定义类型和内置类型,相比于在C语言学习阶段{}一般只用于初始化数组和结构体的

    2024年02月08日
    浏览(35)
  • C++ 11新特性之语法甜点3

    概述         C++ 11中引入了许多简化编程工作的语法上的新特性,我们暂且美其名曰:“语法甜点”。书接上篇,我们继续介绍C++ 11中的这些“语法甜点”,也是第三篇关于“语法甜点”的文章。 语法甜点11:非成员的begin和end         在C++ 03中,标准容器都提供了

    2024年02月20日
    浏览(26)
  • C++ 11新特性之语法甜点4

    概述         C++ 11中引入了许多简化编程工作的语法上的新特性,我们暂且美其名曰:“语法甜点”。书接上篇,我们继续介绍C++ 11中的这些“语法甜点”,也是最后一篇关于“语法甜点”的文章。 语法甜点16:新的字符串字面值         C++ 03提供了两种字符串字面值

    2024年02月20日
    浏览(24)
  • 【C++】C++11新特性的讲解

    新特性讲解第一篇~  文章目录 前言 一、较为重要的新特性 1.统一的初始化列表 2.decltype 3.右值引用+移动语义 总结 C++11 简介 : 在 2003 年 C++ 标准委员会曾经提交了一份技术勘误表 ( 简称 TC1) ,使得 C++03 这个名字已经取代了 C++98 称为 C++11 之前的最新 C++ 标准名称。不

    2024年02月09日
    浏览(28)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包