C++11 std::function

本文深入探讨C++中std::function的使用方法与优势,包括如何封装不同类型的可调用对象,如函数、lambda表达式、仿函数及类成员函数等,实现多态和统一的函数处理方式。
@time    2019-07-07
@author  Ruo_Xiao

1、头文件

#include <functional>

2、作用

        类模版 std::function 是一种通用、多态的函数封装,形成一个新的可调用的 std::function 对象。std::function 的实例可以对任何可以调用对象进行存储复制调用操作。std::function 对象是对 C++ 中现有的可调用实体的一种类型安全的包裹(我们知道像函数指针这类可调用实体,是类型不安全的),就是函数的容器。当我们有了函数的容器之后便能够更加方便的将函数、函数指针作为对象进行处理。

        关于可调用对象的概念:对于一个对象或表达式,如果可以对其使用调用运算符,则称该对象或表达式为可调用对象。

        可调用对象包括:

        (1)函数

        (2)函数指针

        (3)lambda 表达式

        (4)bind 创建的对象

        (5)重载了函数调用运算符的类(仿函数)。

3、std::function 的使用有多态和万总归一的感觉,程序栗子如下:

#include <functional>
#include <iostream>

std::function<void(std::string)> Functional;

// 普通函数。
void TestFunc(std::string str)
{
    std::cout << str << std::endl;
}

// Lambda表达式。
auto lambda = [](std::string str) -> void { std::cout << str << std::endl; };

// 仿函数(functor)。
class Functor
{
public:
    void operator()(std::string str)
    {
        std::cout << str << std::endl;
    }
};

// 1.类成员函数。
// 2.类静态函数。
class TestClass
{
public:
    void ClassMember(std::string str)
    {
        std::cout << str << std::endl;
    }
    static void StaticMember(std::string str)
    {
        std::cout << str << std::endl;
    }
};

int main()
{
    // 普通函数。
    Functional = TestFunc;
    Functional("普通函数");

    // Lambda表达式。
    Functional = lambda;
    Functional("Lambda 表达式");

    // 仿函数。
    Functor testFunctor;
    Functional = testFunctor;
    Functional("仿函数");

    // 类成员函数。
    TestClass testObj;
    Functional = std::bind(&TestClass::ClassMember, testObj, std::placeholders::_1);
    Functional("类成员函数");

    // 类静态函数。
    Functional = TestClass::StaticMember;
    Functional("类静态函数");

    return 0;
}

结果:

普通函数
Lambda 表达式
仿函数
类成员函数
类静态函数

(SAW:Game Over!)

C++11引入了`std::function`作为通用的函数对象封装器,极大地增强了函数对象的灵活性和通用性。然而,尽管其设计和实现经过了标准化,但在实际使用过程中仍然存在一些已知问题或缺陷。 首先,`std::function`在某些情况下可能会导致性能问题。由于`std::function`内部使用了类型擦除技术,它需要进行动态内存分配来存储可调用对象。这种动态分配在某些高性能场景下可能成为瓶颈。此外,调用`std::function`对象时需要进行间接跳转,这可能会导致CPU分支预测失败,从而影响执行效率[^1]。 其次,`std::function`的拷贝语义有时可能会引起误解。虽然`std::function`支持拷贝构造和赋值操作,但这些操作可能会抛出异常,特别是在内部需要进行动态内存分配的情况下。因此,在要求异常安全的代码中使用`std::function`时需要格外小心。 另外,`std::function`在处理具有重载操作符的函数对象时可能会遇到问题。例如,当一个类定义了多个`operator()`时,`std::function`无法自动推导出应该使用哪一个,这会导致编译错误。为了避免这种情况,通常需要显式指定目标函数的类型或使用lambda表达式进行包装。 还有,`std::function`在某些编译器实现中可能存在兼容性问题。例如,某些旧版本的编译器可能没有完全实现`std::function`的所有特性,或者在特定平台上的表现与标准不符。因此,在跨平台开发时需要特别注意编译器的支持情况。 最后,`std::function`的类型信息在运行时会被擦除,这意味着在调试时很难直接从`std::function`对象中获取其内部封装的具体函数信息。这对于调试和日志记录来说是一个挑战。 ### 示例代码 以下是一个简单的示例,展示了如何使用`std::function`来封装不同的可调用对象: ```cpp #include <iostream> #include <functional> void foo(int x) { std::cout << "foo: " << x << std::endl; } struct Bar { void operator()(int x) const { std::cout << "Bar: " << x << std::endl; } }; int main() { std::function<void(int)> func; // 封装普通函数 func = foo; func(10); // 封装函数对象 Bar bar; func = bar; func(20); return 0; } ``` ### 相关问题 1. `std::function`在多线程环境中使用时需要注意哪些问题? 2. 如何避免`std::function`在高性能场景下的性能瓶颈? 3. `std::function`的异常安全性如何保证? 4. 在哪些情况下`std::function`无法正确推导出可调用对象的类型? 5. 如何在调试时获取`std::function`内部封装的具体函数信息? : 由于`std::function`的实现依赖于类型擦除技术,因此在某些情况下可能会导致额外的性能开销。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值