第 18.12 節

std::function

0瀏覽次數0訪問次數--跳出率--平均停留

What problem does this section solve?

Ordinary functions, function pointers, lambdas, and function objects can all be "called" and are collectively referred to as callable objects. However, their specific types are different:

  • Ordinary functions have function types.
  • Function pointers are pointer types.
  • Each Lambda has its own unique type generated by the compiler.
  • Function objects are custom class types.

If you're just calling it once immediately, different types usually aren't a problem; but if you're saving the callback to a member variable, or putting multiple different callbacks into the same container, you'll need a unified type.

std::function is a universal callable object wrapper provided by the standard library.

What is this feature?

std::function<返回值(参数列表)> can store any signature-matching callable objects.

For example, std::function<int(int, int)> indicates: saving a callable object that can be called with two int and returns int.

C++ standard version

std::function has been available since C++11 and requires the header file <functional>.

Common scenarios

Scenewhether it is suitable for std::functionReason
Call the callback immediately upon entering the function.Not necessarily.Template parameters are generally more lightweight.
A class member variable holds a callback.suitable forMember variables need stable types.
vector Save multiple different lambdassuitable forContainer elements must be of the same type.
small functions in high-frequency performance hotspotscautiousstd::function has type erasure overhead
Save non-capturing functions onlyFunction pointers can also be used.But function pointers cannot capture lambdas.

Example code

Example 1: Unified Storage of Different Types of Callable Objects

#include <functional>
#include <iostream>

int add(int a, int b)
{
    return a + b;
}

struct Multiply
{
    int operator()(int a, int b) const
    {
        return a * b;
    }
};

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    // std::function 可以保存普通函数、lambda 或函数对象。
    std::function<int(int, int)> op;

    op = add;
    std::cout << "add: " << op(3, 4) << "\n";

    int (*function_pointer)(int, int) = add;
    op = function_pointer;
    std::cout << "function pointer: " << op(5, 6) << "\n";

    op = [](int a, int b) {
        return a - b;
    };
    std::cout << "lambda: " << op(10, 3) << "\n";

    op = Multiply{};
    std::cout << "function object: " << op(7, 8) << "\n";

    return 0;
}

Results

add: 7
function pointer: 11
lambda: 7
function object: 56

Example 2: Passing std::function as a callback parameter

In this example, calculate doesn't care whether a regular function or a lambda is passed in; it only requires the signature to be int(int, int).

#include <functional>
#include <iostream>

int add(int a, int b)
{
    return a + b;
}

// std::function 可以保存普通函数、lambda 或函数对象。
int calculate(int a, int b, const std::function<int(int, int)>& op)
{
    return op(a, b);
}

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    int x = 10;
    int y = 5;

    int r1 = calculate(x, y, add);
    std::cout << "add result = " << r1 << "\n";

    int r2 = calculate(x, y, [](int a, int b) {
        return a * b;
    });
    std::cout << "multiply result = " << r2 << "\n";

    int offset = 100;
    int r3 = calculate(x, y, [offset](int a, int b) {
        return a + b + offset;
    });
    std::cout << "with offset result = " << r3 << "\n";

    return 0;
}

Results

add result = 15
multiply result = 50
with offset result = 115

Example 3: Save callback to class member variable

Function pointers cannot store lambdas with captures, but std::function can, making it highly suitable for storing event callbacks.

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

class Button
{
    // std::function 可以保存普通函数、lambda 或函数对象。
    std::function<void()> on_click_;

public:
    void set_on_click(std::function<void()> callback)
    {
        on_click_ = std::move(callback);
    }

    void click() const
    {
        if (on_click_)
        {
            on_click_();
        }
        else
        {
            std::cout << "no callback\n";
        }
    }
};

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    Button button;

    button.click();

    std::string name = "Save";
    int count = 0;

    button.set_on_click([name, &count]() {
        ++count;
        std::cout << name << " clicked, count = " << count << "\n";
    });

    button.click();
    button.click();

    return 0;
}

Results

no callback
Save clicked, count = 1
Save clicked, count = 2

Here, name is captured by value, and count is captured by reference. Because both button and count live within main, and count outlives the callback invocation, this example is safe. In real asynchronous scenarios, you need to handle lifecycle management more carefully when storing callbacks.

Example 4: Put multiple different callbacks into the same container

Each Lambda has a different type, so they can't be directly placed in the same vector. Using std::function<void()> gives them a unified type.

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

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    // std::function 可以保存普通函数、lambda 或函数对象。
    // vector 是动态数组,元素数量可以在运行时变化。
    std::vector<std::function<void()>> tasks;

    int total = 0;
    std::string label = "task";

    tasks.push_back([label]() {
        std::cout << label << " A\n";
    });

    tasks.push_back([&total]() {
        total += 10;
        std::cout << "add 10, total = " << total << "\n";
    });

    tasks.push_back([&total]() {
        total *= 2;
        std::cout << "double, total = " << total << "\n";
    });

    for (const auto& task : tasks)
    {
        task();
    }

    std::cout << "final total = " << total << "\n";

    return 0;
}

Results

task A
add 10, total = 10
double, total = 20
final total = 20

Example 5: Template parameters can also accept callbacks when invoked immediately

The advantage of std::function is "preserving and unifying types." If immediate invocation without the need to preserve types is required, function templates are typically more lightweight.

#include <iostream>

template<typename Callback>
void repeat(int times, Callback callback)
{
    for (int i = 0; i < times; ++i)
    {
        callback(i);
    }
}

int main()
{
    // 程序从 main 函数开始执行,下面的语句会按顺序运行。
    repeat(3, [](int i) {
        std::cout << "i = " << i << "\n";
    });

    // 返回 0 表示程序正常结束。
    return 0;
}

Results

i = 0
i = 1
i = 2

Key Grammar Explanation

writing methodMeaningPrecautions
std::function<void()>Save callback without parameters and no return value.Commonly used for buttons, timers, task queues.
std::function<int(int, int)>a callable object that saves two int parameters and returns intSignature must match
if (callback)Check if std::function is emptyEmpty callbacks cannot be directly invoked.
std::move(callback)Move the passed-in callback to a member variable.Avoid unnecessary copying.
vector<std::function<void()>>Saving multiple different types of callbacksUnifying container element types

The relationship between std::function and Lambda in C++.

A Lambda is a callable object, and std::function is a wrapper used to store callable objects. They are not interchangeable.

RequirementRecommendation
Here's a simple conditional logic snippet:
if temperature > 30:
    print("System overheating. Initiating cooling.")
elif temperature < 10:
    print("Temperature too low. Activating heater.")
else:
    print("Temperature within normal operating range.")
```|Lambda|
|Save the callback as a variable or member.|`std::function`|
|put multiple different lambdas into a container|`std::function`|
|Invoked once and performance-sensitive|function template|

## Common Errors

1. Empty `std::function` called directly. Check `if (callback)` first before calling.
2. Signature mismatch. For example, if the callback expects `int(int, int)`, you cannot pass a callable that only accepts a single parameter.
3. When saving a callback, a reference to an already-destroyed local variable was captured.
4. Even for a one-time temporary call, setting the parameter to `std::function` introduces unnecessary overhead in performance-sensitive code.
5. We thought member functions could be directly assigned to `std::function<void(int)>`. However, non-static member functions still require an object. This will be addressed in the next section using `std::bind` and Lambda expressions.

## 使用建议  
- 明确目标:在开始前确定您的具体需求,以便选择最合适的工具或教程。  
- 充分利用资源:参考官方文档、教程和博客,这些资料能帮助您快速上手并解决问题。  
- 实践应用:通过动手操作项目或编写代码来巩固学习成果,提升实际操作能力。  
- 问题解决:遇到困难时,查阅参考资料或寻求社区支持,逐步培养独立解决问题的能力。  
- 分享经验:完成项目后,可以撰写文章或博客分享心得,帮助其他学习者。  

如果需要针对特定领域(如单片机、机器人或环境搭建)的进一步建议,请提供更多信息,我将为您细化内容。

1. When you need to save a callback, use `std::function`.
2. Use `std::function` when unifying different types of callbacks.
3. When invoking a callback immediately, template parameters may be preferred.
4. Pay particular attention to the lifecycle of captured objects when saving callbacks.
5. Don't treat `std::function` as the sole answer for all callback scenarios. It's a tool for clarity and flexibility, but it does have some runtime overhead.

## Summary

- Callable objects include regular functions, function pointers, Lambda expressions, and function objects.
- `std::function<返回值(参数...)>` Store signature-matching callable objects using a unified type.
- `std::function` is suitable for storing callbacks, serving as member variables, and being placed in containers.
- An empty `std::function` cannot be called.
- A once-off callback typically benefits from lighter-weight template parameters.
音乐页