C# 通用委托的 C++ 等效语法,以及与 lambda 的用法

C++ equivalent syntax for a C# generic delegate, and usage with lambdas

在 C# 中我可以这样做:

delegate void myFunctionDelegate<T>(T arg);

在 C++ 中,我知道我需要为函数指针的模板使用别名,但语法太奇怪了,我发现的所有示例都让我更加困惑。

以下是错误的;我该如何更正它?

template<typename T>
using myFunctionDelegate = void (*)(T arg);

我想这样使用它:

template<class T> class Foo
{
    ...
    void someOtherFunction(myFunctionDelegate callback)
    {
        ...
        callback(someValue);
    }
}

然后:

myClassInstance.someOtherFunction([&](T arg) {
    // do something with the callback argument
});

std::function<void(T)> myFunctionDelegate 是(非常)粗略的 delegate void myFunctionDelegate<T>(T arg)

std::function<void(T)> 遵循值语义(它的行为更像 int 而不是 C# 对象引用),这使事情变得不同。

生命周期(或其副本)超过本地作用域的 lambda 闭包 ([](T t){/*code*/}) 不应使用基于 & 的捕获。而是使用基于 = 的捕获(这可能需要额外的工作)。如果您调用的代码在调用的生命周期之后不存储委托的副本,则 [&] 是最佳选择。在 C++ 中,数据的生命周期是您需要关心的事情。

这并不是关于 lambda 和 std::function 如何工作的完整教程,而只是为您指明正确的方向。

你所拥有的几乎在句法上是可行的; myFunctionDelegate 的使用只需要一个类型参数:

void someOtherFunction(myFunctionDelegate<T> callback)
                                         ^^^

如果您没有从中获得任何特别的好处,别名参数名称是可选的:

template<typename T>
using myFunctionDelegate = void(*)(T);

然而,还有一个更大的问题:函数指针不处理状态。示例调用中使用的 lambda 通过捕获它来使用状态。因此,捕获的 lambda 不能转换为函数指针。当传递这样的 lambda 如此方便时,函数参数应该支持它。

有两种常用的方法。首先是忘记强制使用特定的 return 和参数类型。相反,让调用者传递任何对象(lambda、函数指针、仿函数、std::bind 的结果),这些对象可以按照你的函数调用它的方式调用:

template<typename Callable>
void someOtherFunction(Callable callback) {
    ...
    callback(someValue);
}

如果调用不起作用,代码将无法编译1(不幸的是,错误并没有太大帮助,但未来 Concepts添加可以很容易地帮助那里。

另一方面,您可能希望明确指定函数类型。 C++ 有一个通用类型来存储任何可调用对象(参见上面的列表)。该类型是 std::function。它比简单的模板参数更重量级,但在您需要时很有用。

template<typename T>
using myFunctionDelegate = std::function<void(T)>;

void someOtherFunction(const myFunctionDelegate<T> &callback) {...}

[1]:这并不总是正确的(参见 SFINAE),但就您而言可能是正确的。