【发布时间】:2019-05-30 19:23:12
【问题描述】:
我正在编写一个模板化的事件处理程序,它允许用户将事件(以结构的形式)发送到连接的侦听器。
我有一个“emit”成员函数,它将从提供的可变参数模板参数构造一个 Event 对象并转发该事件。但是,我想提供一个专门的“emit”函数来检测提供的参数是否是预先构造的 Event 对象,在这种情况下,我可以转发该事件而无需制作多余的副本。
我最初的尝试...
template <typename T_Event>
class EventHandler
{
public:
template <typename... T_Args>
void emit(T_Args&&... args)
{
printf("variadic\n");
deliver(T_Event {std::forward<T_Args>(args)...});
}
void emit(const T_Event& event)
{
printf("reference\n");
deliver(event);
}
...
};
我使用以下逻辑尝试了这两个 emit 函数,但结果发现可变参数模板函数总是优先于 const 引用函数。
struct Event { int x; };
EventHandler<Event> handler;
Event event {1};
handler.emit(event);
handler.emit(2);
经过更多研究后,我通过定义可变参数模板函数的两个版本并使用 enable_if 执行正确的一个来实现我的目标。
template <typename... T_Args>
void emit(T_Args&&... args)
{
printf("variadic\n");
T_Event event {std::forward<T_Args>(args)...};
deliver(event);
}
template <typename... T_Args, typename = std::enable_if<std::is_same<const T_Event&, T_Args...>::value>>
void emit(T_Args&&... args)
{
printf("reference\n");
deliver(std::forward<T_Args>(args)...);
}
当我使用 GCC 编译时,此解决方案完全符合我的需要,但如果我使用 CLANG 编译,我会收到以下错误消息:
call to member function 'emit' is ambiguous
handler.emit(event);
candidate function [with T_Args = <EventB &>]
void emit(T_Args&&... args)
candidate function [with T_Args = <EventB &>, $1 =
std::__1::enable_if<false, void>]
void emit(T_Args&&... args)
我假设我接近正确的解决方案,谁能解释我做错了什么?
【问题讨论】:
-
正确的方法是
const T_Event&和T_Event&&。 -
问题是 T_Event&& 仍然需要在调用发射函数之前构造,而不是能够为发射函数提供原始事件构造函数参数。
-
invoke是指std::invoke吗? -
不,这是类中的另一个函数。为了避免混淆,我将在示例中对其进行重命名。
标签: c++ templates variadic-templates