C++ 使用变量捕获的多回调结构的习惯用法

C++ 使用变量捕获的多回调结构的习惯用法,c++,callback,inversion-of-control,C++,Callback,Inversion Of Control,我有一个函数,它接受一个对象并调用它上的各种回调函数作为它的输出。例如: template<typename T> void iterateInParallel(vector<int> const& a, vector<int> const& b, T && visitor) { // sometimes invokes visitor.fromA(i) // sometimes invokes visitor.

我有一个函数,它接受一个对象并调用它上的各种回调函数作为它的输出。例如:

template<typename T>
void iterateInParallel(vector<int> const& a, vector<int> const& b, T && visitor)
{
    // sometimes invokes visitor.fromA(i)
    // sometimes invokes visitor.fromB(i)
    // sometimes invokes visitor.fromBoth(i, j)
}
这比旧的函子方法要好得多,在函子方法中,我定义了一个类,它在构造函数中引用了
sum
numElems
。和functor方法一样,它通常具有零运行时成本,因为回调代码在实例化函数时是已知的

但是lambda只是一个函数,因此对于上面的
iterateInParallel
方法来说是不可行的;我又开始复制参考文献了

因此,我要寻找的是一种方法,可以大致获得lambdas默认捕获的便利性、性能和惯用性,同时仍然能够调用多种类型的回调

我考虑过的选择:

  • 传递多个回调函数。这还不算太糟,通常我都是这么做的。但是,很容易混淆参数顺序,而且它实际上会使参数列表膨胀。由于回调的名称不在用户代码中,因此它也不太需要自我记录
  • 重写内部函数,使其调用一个回调,并使用参数帮助定义回调的类型。丑陋的,骇人的,不是一个值得尊敬的程序员会做的那种事情
  • 传递一个包含一大堆
    std::function
    s的结构。我觉得我可以让它看起来很好,但我也觉得它最终会执行堆分配和每次调用间接寻址
  • 涉及预处理器的一些古怪的事情。天哪
并一起提供合适的解决方案:

#include <iostream>

template<class F1, class F2>
struct Visitor
{
    F1 fromA;
    F2 fromB;
};
template<class F1, class F2>
Visitor(F1, F2) -> Visitor<F1, F2>;


template<class F1, class F2>
void f(Visitor<F1, F2> v)
{
    v.fromA(1);
    v.fromB("hello");
}


int main()
{
    f( Visitor{
        .fromA = [](int n){ std::cout << "A: " << n << "\n"; },
        .fromB = [](std::string_view v){ std::cout << "B: " << v << "\n"; }
    });
}
#包括
样板
结构访问者
{
F1来自a;
F2 fromB;
};
样板
访客(F1、F2)->访客;
样板
f(访客五)
{
v、 fromA(1);
v、 fromB(“你好”);
}
int main()
{
f(访客){

.fromA=[](int n){std::cout这还不错,但需要一个尚未完成的标准修订版。此外,这似乎阻止了
f
使用“正常”函子。并且缺少限制
F1
/
F2
;-)的概念@Jarod42你可以在这里传递这个习语;)但是可以。@Sneftel:
F1
/
F2
可以是常规的函子。如果你在
f
中使用
std::invoke
而不是
operator()
,你甚至可以传递方法上的指针:-)@Jarod42对不起,我应该说“一个普通的结构”与中一样,
f
不再是完全参数化的,但需要一个作为
visitor
实例的访问者。
#include <iostream>

template<class F1, class F2>
struct Visitor
{
    F1 fromA;
    F2 fromB;
};
template<class F1, class F2>
Visitor(F1, F2) -> Visitor<F1, F2>;


template<class F1, class F2>
void f(Visitor<F1, F2> v)
{
    v.fromA(1);
    v.fromB("hello");
}


int main()
{
    f( Visitor{
        .fromA = [](int n){ std::cout << "A: " << n << "\n"; },
        .fromB = [](std::string_view v){ std::cout << "B: " << v << "\n"; }
    });
}