在C++中执行一个函数作为参数
Execute a function as parameter in C++
假设我们有类Obj
和这个main:
class Obj
{
public:
void func1(int n) {}
void func2(std:string n) {}
};
std::vector<Obj> retrieveObjs()
{
std::vector<Obj> result;
// ...
return result;
}
int main()
{
// Call func1 for all obj
{
auto objs = retrieveObjs();
for (auto& obj : objs)
{
obj.func1(100);
}
}
// Call func2 for all obj
{
auto objs = retrieveObjs();
for (auto& obj : objs)
{
obj.func2("xxx");
}
}
return 0;
}
我想要一个通用函数来从所有对象中调用特定函数,如下面的伪代码。
void invokeAll(FUNCTION f, PARAM p) // pseudocode
{
auto objs = retrieveObjs();
for (auto& obj : objs)
{
obj.f(p);
}
}
int main() // pseudocode
{
invokeAll(func1, 100);
invokeAll(func2, "xxx");
}
我不知道如何替换FUNCTION
和PARAM
以使其工作。
使用template/lambda/for_each或类似的技巧可以做到这一点吗?
您所描述的是指向成员函数的指针的一个很好的用例,语法如下:
// get the pointer to the function.
auto funPtr = &Obj::func1;
Obj obj;
// call the method using the function pointer
obj.(*funPtr)();
在您的情况下,您可以将函数指针作为参数接收,将参数作为包接收。
// F is the type of the function pointer.
// As arguments and return type of `f` can change, so it's type `F` can.
template<typename F, typename... Args>
void invokeAll(F f, Args... args) {
for (auto&& obj : retrieveObjs()) {
// We call member `f` with `obj`
// We expand the pack `args` to send it as multiple arguments
obj.(*f)(args...);
}
}
您将能够以您想要的类似方式调用该函数:
// Notice the member function pointer syntax
invokeAll(&Obj::func1, 100);
// Work with multiple arguments, [100, "test"] will be packed into `args`
invokeAll(&Obj::func2, 100, "test");
在C++17中,使用std::invoke
,您可以通过允许任何类型的以Obj
为参数的函数来进一步推广您的情况:
template<typename F, typename... Args>
void invokeAll(F f, Args... args) {
for (auto&& obj : retrieveObjs()) {
// invoke function `f` with `obj` as it's object and `args` as parameter.
std::invoke(f, obj, args...);
}
}
如果你现在想支持更多类型的功能,包括lambdas,你可以使用void_t
风格的sfinae:
// The compiler will pick this function if `obj.(*f)(args...)` can compile
template<typename F, typename... Args>
auto invokeAll(F f, Args... args) -> void_t<decltype(std::declval<Obj>().(*f)(args...))> {
// Here's the constraint ------^
for (auto&& obj : retrieveObjs()) {
obj.(*f)(args...);
}
}
// The compiler will pick this function if `f(obj, args...)` can compile
template<typename F, typename... Args>
auto invokeAll(F f, Args... args) -> void_t<decltype(f(std::declval<Obj>(), args...))> {
// Here's the constraint ------^
for (auto&& obj : retrieveObjs()) {
f(obj, args...);
}
}
void_t
定义如下:
template<typename...>
using void_t = void;
然后,有了这个,你也可以解锁这个语法:
invokeAll([](Obj& obj, int a){
// this block will be called for each `obj` in `retrieveObjs`
}, 100);
如果您也想支持不可复制的类型,请查找完美转发。
template<class F, class R>
void invoke_on_range( F&& f, R&& r ) {
std::for_each( r.begin(), r.end(), std::forward<F>(f) );
}
这会获取一个范围,并在该范围的每个元素上调用lambda。
int main() {
invoke_on_range( [](Obj& obj){ obj.func1(100); }, retrieveObjs() );
invoke_on_range( [](Obj& obj){ obj.func2("xxx"); }, retrieveObjs() );
}
编写lambda有一些样板,但结构不是你的问题。
我有时也觉得这很有用:
template<class F, class...Args>
void invoke_on_each( F&& f, Args&&...args ) {
using discard=int[];
(void)discard{0,(void(
f( std::forward<Args>(args) )
),0)...};
}
这需要一个lambda f
和一组args...
。它在每个args...
上运行一次f
。奇怪的discard
技巧包括制作一个所有0
的数组并将其丢弃(优化器不会这样做(,以便生成一个上下文,在该上下文中...
将完全按照我们的意愿进行操作。
隐藏您在retrieveObjs
上操作的事实似乎不值得编写另一个包装函数,但也可以这样做。
如果您想从实现中分离接口,您可以用std::function<void(Obj&)>
替换class F
和F&&
,以获得适度的性能成本。
相关文章:
- 函数向量_指针有不同的原型,我可以构建一个吗
- 为什么在没有显式默认构造函数的情况下,将另一个结构封装在联合中作为成员的结构不能编译
- 创建一个函数以在输入为负数或零时输出字符串.第一次执行用户定义的函数
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 如何仅为一个函数添加延迟
- 构造函数正在调用一个使用当前类类型的函数
- C++-试图将函数指针推回到另一个CPP文件中的矢量时出错
- 有一个打印语句的函数是一种糟糕的编程实践吗
- 有没有什么方法可以使用一个函数中定义的常量变量,也可以由c++中同一程序中的其他函数使用
- 输入到文件并输出到另一个文件,并将流文件传递给函数
- 我不明白为什么我声明一个空的内部结构并将其传递给构造函数
- 如何创建函数管道,以便函数一个接一个地运行?
- 如何巧妙地编写两个函数——一个用于检查是否存在解决方案,另一个用于获取所有解决方案
- 在c++中的复制构造函数/一个声明语句中的初始化的延续中使用chain方法
- C :基类调用自己的虚拟函数 - 一个反图案
- 如何在这个交换函数(一个单独的链表)中找到错误
- 两个相同的函数(一个使用模板模式,另一个不使用)
- 你怎么能一次给一个函数一个参数呢
- 为什么要做两个函数?(一个是非const,另一个是const)
- 当代码在其他地方使用时,如何保证函数一个接一个地被调用