C++:使用 void 作为模板参数
C++: use void as template argument
我有这个最小的 class 来表示客户端可以订阅的事件。
该事件可以有一个与之关联的数据类型,因此当它被发布者触发时,该类型的参数将传递给客户端的回调:
template<typename Arg, typename Callback = function<void(const Arg&)>>
class Event
{
public:
Event(Callback c) : mCallback(c){}
void Trigger(const Arg& arg) {
mCallback(arg);
}
private:
Callback mCallback;
};
现在我可以创建一个 Event<int>
或任何其他具体类型,但允许“空”事件对我来说真的很重要,它没有与之关联的数据:Event<void>
但遗憾的是,这不起作用:
static void FooVoid() {
cout << "Look ma, no args!" << endl;
}
static void FooInt(int a) {
cout << "int arg " << a << endl;
}
int main()
{
/* Compiles */
Event<int> eInt(&FooInt);
eInt.Trigger(42);
/* Does not compile :(
Event<void> eVoid(&FooVoid);
eVoid.Trigger();
*/
return 0;
}
有什么方法可以实现这个愿望API?怎么样?
(P.S 该解决方案应该适用于 C++11)
解决此问题的最快方法是使用 parameter pack (added in C++11) for your template argument instead of a single type and using an empty parameter pack instead of void
. A parameter pack can homogeneously hold any number of type, including 0 and 1. Then it can be used to generate the right types and member functions. You basically just have to add ...
correctly near every use of Arg
(link) :
#include <functional>
#include <iostream>
template<typename ... Arg>
class Event
{
public:
using Callback = std::function<void(const Arg&...)>;
Event(Callback c) : mCallback(c){}
void Trigger(const Arg& ... arg) {
mCallback(arg...);
}
private:
Callback mCallback;
};
static void FooVoid() {
std::cout << "Look ma, no args!" << std::endl;
}
static void FooInt(int a) {
std::cout << "int arg " << a << std::endl;
}
int main()
{
/* Compiles */
Event<int> eInt(&FooInt);
eInt.Trigger(42);
Event<> eVoid(&FooVoid);
eVoid.Trigger();
return 0;
}
这还有一个好处,就是您可以使用带有多个参数的回调。如果这不是您想要的,您可以添加一个 static_assert
来防止它:
template<typename ... Arg>
class Event
{
public:
using Callback = std::function<void(const Arg&...)>;
static_assert(sizeof...(Arg) <= 1, "Too many arguments");
Event(Callback c) : mCallback(c){}
void Trigger(const Arg& ... arg) {
mCallback(arg...);
}
private:
Callback mCallback;
};
请注意,此解决方案需要 Event<>
而不是 Event<void>
。您可以通过为 Event<void>
添加一个使用 Event<>
(link) :
的简短专业化来解决这个问题
template<>
class Event<void> : public Event<>
{
// Inherit constructors
using Event<>::Event;
};
我有这个最小的 class 来表示客户端可以订阅的事件。
该事件可以有一个与之关联的数据类型,因此当它被发布者触发时,该类型的参数将传递给客户端的回调:
template<typename Arg, typename Callback = function<void(const Arg&)>>
class Event
{
public:
Event(Callback c) : mCallback(c){}
void Trigger(const Arg& arg) {
mCallback(arg);
}
private:
Callback mCallback;
};
现在我可以创建一个 Event<int>
或任何其他具体类型,但允许“空”事件对我来说真的很重要,它没有与之关联的数据:Event<void>
但遗憾的是,这不起作用:
static void FooVoid() {
cout << "Look ma, no args!" << endl;
}
static void FooInt(int a) {
cout << "int arg " << a << endl;
}
int main()
{
/* Compiles */
Event<int> eInt(&FooInt);
eInt.Trigger(42);
/* Does not compile :(
Event<void> eVoid(&FooVoid);
eVoid.Trigger();
*/
return 0;
}
有什么方法可以实现这个愿望API?怎么样?
(P.S 该解决方案应该适用于 C++11)
解决此问题的最快方法是使用 parameter pack (added in C++11) for your template argument instead of a single type and using an empty parameter pack instead of void
. A parameter pack can homogeneously hold any number of type, including 0 and 1. Then it can be used to generate the right types and member functions. You basically just have to add ...
correctly near every use of Arg
(link) :
#include <functional>
#include <iostream>
template<typename ... Arg>
class Event
{
public:
using Callback = std::function<void(const Arg&...)>;
Event(Callback c) : mCallback(c){}
void Trigger(const Arg& ... arg) {
mCallback(arg...);
}
private:
Callback mCallback;
};
static void FooVoid() {
std::cout << "Look ma, no args!" << std::endl;
}
static void FooInt(int a) {
std::cout << "int arg " << a << std::endl;
}
int main()
{
/* Compiles */
Event<int> eInt(&FooInt);
eInt.Trigger(42);
Event<> eVoid(&FooVoid);
eVoid.Trigger();
return 0;
}
这还有一个好处,就是您可以使用带有多个参数的回调。如果这不是您想要的,您可以添加一个 static_assert
来防止它:
template<typename ... Arg>
class Event
{
public:
using Callback = std::function<void(const Arg&...)>;
static_assert(sizeof...(Arg) <= 1, "Too many arguments");
Event(Callback c) : mCallback(c){}
void Trigger(const Arg& ... arg) {
mCallback(arg...);
}
private:
Callback mCallback;
};
请注意,此解决方案需要 Event<>
而不是 Event<void>
。您可以通过为 Event<void>
添加一个使用 Event<>
(link) :
template<>
class Event<void> : public Event<>
{
// Inherit constructors
using Event<>::Event;
};