【问题标题】:Template of template parameter with GCC使用 GCC 的模板参数模板
【发布时间】:2014-02-09 12:44:09
【问题描述】:

我正在尝试使用模板的模板作为参数,当我使用 clang 编译时一切正常,但是当我尝试使用 GCC 4.8 时出现以下错误:

无法从非模板类型“DamageAnimationSystem”推导出“TReceiver”的模板

这是我的情况:我有一个程序,其中类可以订阅事件管理器来处理某些类型的事件,但不是全部。

为此,我从具有特定类型的类继承:

 class DamageAnimationSystem : public Receiver<DamageEvent>, 
                               public Receiver<HealthEvent>

在这里,我的班级会监听“DamageEvent”和“HealthEvent”,所以我需要为此类事件声明虚拟方法:

DamageAnimationSystem::onEvent( const DamageEvent& event ) {}
DamageAnimationSystem::onEvent( const HealthEvent& event ) {}

我需要订阅这个活动:

DamageAnimationSystem::DamageAnimationSystem()
{
    eventManager->subscribe<DamageEvent>(this);
    eventManager->subscribe<HealthEvent>(this);
}

正如我之前所说,使用 Clang 时一切正常,但使用 GCC 时出现上述错误。

这是我所做的:

接收者:

class EBaseReceiver
{
protected:
    static std::size_t nextId;
};

template <typename TEventData>
class Receiver : public EBaseReceiver
{
friend class EventManager;
public:
     virtual void onEventReceive( const TEventData& event ) = 0;
     static std::size_t getId() { return ID; }
private:
     static std::size_t ID;
};

事件:

struct BaseEvent
{
protected:
     static std::size_t nextId;
};

template <typename T>
struct Event : public BaseEvent
{
     static std::size_t getId() { return ID; }
     static std::size_t ID;
};

**And finally the event manager:**
template< class TEvent, template<class> class TReceiver>
void EventManager::subscribe( TReceiver<TEvent>* receiver );
{
    const std::size_t eventId = TEvent::getId();
    this->subscribers[eventId].push_back(receiver);
}

我已经在线模拟测试结果:https://ideone.com/vZZhqN

非常感谢您的帮助!

PS:我需要与 GCC 兼容,因为我需要将此代码与 android NDK 一起使用,并且线程在最后一个 NDK 上无法与 clang 一起使用。

编辑

我正在尝试对 std::function 和 std::bind 使用另一种方法:

接收者:

class Receiver {};

事件:

template <typename T>
struct Event : public BaseEvent
{
    friend class EventManager;
    static std::size_t getId() { return ID; }
private:            
    typedef std::function<void( const T& event )> EventCallback;
    static std::size_t ID;
    static std::vector<EventCallback> listeners;
};

** 事件管理器 **

////////////////////////////////////////////////////////////
template <typename TEvent>
void emit( const TEvent& event )
{
    const auto& listeners = TEvent::listeners;
    for( const auto& listener : listeners )
        listener(event);
}

////////////////////////////////////////////////////////////
template< class TEvent, class TReceiver>
void subscribe( TReceiver* receiver )
{
    TEvent::listeners.push_back(std::bind( &TReceiver::onEventReceive, receiver, std::placeholders::_1));
}

我在 std::bind 上遇到以下错误:

No matching function for call to 'bind'
Candidate template ignored: couldn't infer template argument '_Fp'
Candidate template ignored: couldn't infer template argument '_Rp'

再次感谢!

【问题讨论】:

  • 您能添加引发事件的EventManager 方法吗?那就是调用虚函数的地方,这就是错误可能所在的地方。我认为您应该在接收器中使用虚拟继承:您从多个不同的接收器继承,并且接收器的 vtable 应该分派到正确的接收器类,不是吗?
  • OTOH 这个问题的近似值更容易实现。考虑一下:哪个是真正的事件?回调的集合,接收者只是一个回调。但回调不仅仅是一个函数,它可以是任何可调用实体。所以我建议你使用std::function 而不是接收器类。稍微玩一下这个概念,您可以创建一个事件类,它允许任何类型的可调用实体成为接收器(函数指针、lambda、仿函数、成员函数、全局函数等)。
  • 你真的有不止一个Reciever类型class TEMPLATE需要支持吗?删除TReciever,将参数改为Reciever&lt;…&gt;
  • 问题可以归结为以下ideone.com/lSkS80clang是错误的接受。 换句话说; 您的代码格式错误
  • 感谢您的回复。 @Manu343726:确实,我终于尝试使用 std::function 和 std::bind 来解决问题,但看起来 std::bind 不喜欢模板参数(我用我的更新编辑了我的帖子制作)。 Yakk:我试过了,但在这之后,编译器不知道接收器的类型,并且使用了来自“EBaseReceiver”的错误 ID(我在模拟期间没有收到正确的事件)。 Refp:所以 Clang 是错误的,我会尝试像 Manu343726 说的那样使用函数和绑定。

标签: c++ templates gcc clang


【解决方案1】:

以下编译(不要链接):https://ideone.com/EheCok

我以这种方式明确了模板参数:

eventManager.subscribe<HealthEvent, ::Receiver>(this);
eventManager.subscribe<DamageEvent, ::Receiver>(this);

【讨论】:

    猜你喜欢
    • 2012-01-20
    • 1970-01-01
    • 2019-11-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多