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),但就您而言可能是正确的。
在 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),但就您而言可能是正确的。