C++可调用类型

2023/07/12 C++ 共 2885 字,约 9 分钟

C++可调用类型

基本概念

可调用(Callable) 类型是可应用 INVOKEINVOKE 操作(例如用于 std::functionstd::bindstd::thread::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

文档信息

Search

    Table of Contents