C++可调用类型分类及std::invoke首参可接受类型咨询
C++可调用类型与std::invoke参数详解
嘿,我来帮你理清这些C++里的可调用类型和std::invoke的参数问题~
一、C++中的可调用类型
在C++里,只要是能通过()运算符(或配合对象实例)完成调用的类型,都属于可调用类型,具体包括:
- 普通函数(全局函数、命名空间内的函数都算)
- 函数指针(指向普通函数、静态成员函数的指针)
- 函数引用(对普通函数的引用)
- 非静态成员函数指针(注意这类指针调用时必须搭配类的对象实例)
- 非静态成员函数引用
- 仿函数(重载了
operator()的类/结构体的实例,比如std::function就是典型的包装类仿函数) - Lambda表达式(本质是编译器生成的匿名仿函数实例,不管有没有捕获变量都算)
std::bind返回的绑定表达式对象- 类的静态成员函数(可以当作普通函数指针来使用)
- 自定义可调用对象(只要重载了合适的
operator()就行,比如某些智能指针包装的对象)
二、std::invoke可接受的第一个参数类型(满足std::is_invokable_v<F, Args...>为true的F)
std::invoke的设计就是统一处理各种可调用类型的调用,所以所有上面提到的可调用类型,只要搭配合适的Args参数,都可以作为它的第一个参数。我们可以按类型层级梳理:
1. 基础函数相关类型
- 普通函数本身:直接传函数名就能调用,比如
std::invoke(my_func, arg1, arg2); - 函数指针:不管是指向普通函数还是静态成员函数的指针都没问题,比如:
void (*fp)() = &my_func; std::invoke(fp); - 函数引用:用法和函数指针类似,比如:
void (&fr)() = my_func; std::invoke(fr);
2. 类成员相关类型
这是std::invoke特别处理的场景,普通的函数调用语法没法直接用成员指针,但invoke可以:
- 非静态成员函数指针:需要在
Args里传入类的对象实例(可以是对象本身、指针或者引用),比如:std::invoke(&MyClass::my_method, obj_instance, arg); std::invoke(&MyClass::my_method, &obj_instance, arg); // 传指针也可以 - 非静态数据成员指针:这时候
invoke会返回该成员的值,同样需要传入对象实例,比如:std::invoke(&MyClass::my_member, obj_instance);
3. 用户自定义可调用对象
这类类型的灵活性最高,只要符合可调用规则就行:
- 仿函数实例:比如自定义的结构体重载
operator()后,直接传实例即可:struct Foo { void operator()() { /* do something */ } }; Foo foo; std::invoke(foo); - Lambda表达式:直接传Lambda实例,捕获变量的Lambda也完全支持:
int x = 10; std::invoke([x](){ std::cout << x << std::endl; }); std::function包装对象:作为可调用类型的通用包装,自然可以被invoke处理:std::function<int(int)> f = [](int a){ return a*2; }; std::invoke(f, 5); // 返回10std::bind返回的绑定对象:绑定后的参数可以直接通过invoke触发调用:auto bound_func = std::bind(my_func, arg1, std::placeholders::_1); std::invoke(bound_func, arg2);
简单来说,std::is_invokable_v<F, Args...>为true的核心就是:给定F和Args,std::invoke(F, Args...)是合法的C++代码。所以所有能被invoke合法调用的类型,都满足这个条件。
内容的提问来源于stack exchange,提问作者Vincent




