C++可调用类型
基本概念
可调用(Callable) 类型是可应用 INVOKE 和 INVOKE 操作(例如用于 std::function、std::bind 和 std:
:thread)的类型。INVOKE是C++17版本引入的类,其定义参考链接。类似于通用的调用可调用对象的模板类。
可调用类型的要求
如果满足下列条件,那么类型 T 是可调用 (Callable) 的:
给定
-
T类型的对象f - 适合的实参类型列表
ArgTypes - 适合的返回类型
R
那么下列表达式必须合法:
| 表达式 | 要求 |
|---|---|
INVOKE(f, std::declval |
该表达式在不求值语境中良构 |
注解
数据成员指针是可调用 (Callable) 的,尽管实际上并不发生函数调用。
标准库
此外,下列标准库设施接受任何可调用 (Callable) 类型(不仅是函数对象 (FunctionObject) ):
| function(C++11) | 包装具有指定函数调用签名的任意可复制构造类型的可调用对象 (类模板) |
|---|---|
| move_only_function(C++23) | 包装具有指定函数调用签名的任意类型的可调用对象 (类模板) |
| bind(C++11) | 绑定一或多个实参到函数对象 (函数模板) |
| bind_front(C++20) | 按顺序绑定一定数量的参数到函数对象 (函数模板) |
| reference_wrapper(C++11) | 可复制构造 (CopyConstructible) 且可复制赋值 (CopyAssignable) 的引用包装器 (类模板) |
| result_ofinvoke_result(C++11)(C++20 中移除)(C++17) | 推导以一组实参调用一个可调用对象的结果类型 (类模板) |
| thread(C++11) | 管理单独的线程 (类) |
| jthread(C++20) | 有自动合并和取消支持的 std::thread (类) |
| call_once(C++11) | 仅调用函数一次,即使从多个线程调用 (函数模板) |
| async(C++11) | 异步运行一个函数(有可能在新线程中执行),并返回保有它的结果的 std::future (函数模板) |
| packaged_task(C++11) | 打包一个函数,存储其返回值以进行异步获取 (类模板) |
典型可调用类型的使用实例
调用函数对象
#include <functional>
#include <iostream>
struct PrintNum
{
void operator()(int i) const
{
std::cout << i << '\n';
}
};
int main()
{
// 调用函数对象
std::invoke(PrintNum(), 18);
return 0;
}
普通函数
#include <functional>
#include <iostream>
void print_num(int i)
{
std::cout << i << '\n';
}
int main()
{
// 调用自由函数
std::invoke(print_num, -9);
return 0;
}
成员函数
#include <functional>
#include <iostream>
struct Foo
{
Foo(int num) : num_(num) {}
void print_add(int i) const { std::cout << num_ + i << '\n'; }
int num_;
};
int main()
{
// 调用成员函数
const Foo foo(314159);
std::invoke(&Foo::print_add, foo, 1);
// 调用(访问)数据成员,数据成员是可调用的,尽管不发生实际的函数调用。
std::cout << "num_:" << std::invoke(&Foo::num_, foo) << '\n';
return 0;
}
lambda函数
#include <functional>
#include <iostream>
void print_num(int i)
{
std::cout << i << '\n';
}
int main()
{
// 调用 lambda
std::invoke([]() { print_num(42); });
return 0;
}
std::funciton
可调用类型种类很多,需要有一个把所有 callable 对象封装成统一形式的类型模板的方式,std::function 的实例可以对任何可以调用的目标实体进行存储, 复制, 和调用操作, 实现一种类型安全的包裹。
在标头 <functional> 定义 |
||
|---|---|---|
| template< class > class function; /* 未定义 */ | (C++11 起) | |
| template< class R, class… Args > class function<R(Args…)>; | (C++11 起) |
类模板 std::function 是通用多态函数包装器。 std::function 的实例能存储、复制及调用任何可复制构造 (CopyConstructible) 的可调用 (Callable) 目标——函数(通过其指针)、 lambda 表达式、 bind 表达式或其他函数对象,还有指向成员函数指针和指向数据成员指针。
存储的可调用对象被称为 std::function 的目标。若 std::function 不含目标,则称它为空。调用空 std::function 的目标导致抛出 std::bad_function_call 异常。
std::function 满足可复制构造 (CopyConstructible) 和可复制赋值 (CopyAssignable) 。
#include <functional>
#include <iostream>
void print_num(int i)
{
std::cout << i << '\n';
}
int main()
{
// 存储自由函数
std::function<void(int)> f_display = print_num;
std::invoke(f_display, -9);
// 存储 lambda
std::function<void()> f_display_42 = []() { print_num(42); };
std::invoke(f_display_42);
// 存储到 std::bind 调用的结果
std::function<void()> f_display_31337 = std::bind(print_num, 31337);
std::invoke(f_display_31337);
return 0;
}
参考
- https://zh.cppreference.com/w/cpp/named_req/Callable
- https://mp.weixin.qq.com/s/gkjt6DeHjVPKyVStF9-fBA
文档信息
- 本文作者:JianZheng
- 本文链接:https://zhengjian526.github.io/left-handed_knife//2023/07/12/C++-callable-type/
- 版权声明:自由转载-非商用-非衍生-保持署名(创意共享3.0许可证)