Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Ambigous member request from variadic template

This makes no sense to me. GCC is complaining that the call below in main() to processMsg() is ambiguous, even though all of the template-created processMsg() calls are reported back as candidates. I've tried implementing this variadic template prototype three different ways and they all lead back to this same issue of ambiguous request. I did get closer when I broke the template implementation up into different cases for and but then I could the compiler could only resolve the first lookup in the tuple.

I've pasted a small example. I'm sure I'm missing something simple....

#include <tuple>

//----------------------------------------------------------------------
//
class MessageBase
{
  public:
    MessageBase( const int _id ) : m_id( _id ) {}
    virtual int getMessageID() const { return( m_id ); }

  private:
    const int m_id;
};

#define MESSAGE( NAME, VAL ) \
  class Message##NAME : public MessageBase { \
    public: \
      Message##NAME() : MessageBase( VAL ) { } \
  };

  MESSAGE( One, 1 );
  MESSAGE( Two, 2 );
  MESSAGE( Ten, 10 );

//----------------------------------------------------------------------
//

template< typename T > 
struct MyMessageInterface {
    virtual void processMsg( const T& t ) { } 
};

template< typename... T > 
struct MyMessageHandler : public MyMessageInterface< T >...
{};

template< typename... T > 
struct MyMessageHandler< std::tuple< T... > > 
  : public MyMessageInterface< T >...
{};


//----------------------------------------------------------------------
//
typedef std::tuple< MessageOne, MessageTwo, MessageTen > Foople;

int main()
{
  MyMessageHandler< Foople > mmh;
  mmh.processMsg( MessageOne() );
}
like image 938
RMHarris157 Avatar asked Jan 10 '23 06:01

RMHarris157


2 Answers

You could add a forwarder to the specialization of MyMessageHandler:

template< typename... T > 
struct MyMessageHandler< std::tuple< T... > > 
  : public MyMessageInterface< T >...
{
    template< typename U >
    void processMsg( const U& u )
    {
        MyMessageInterface< U >::processMsg( u );
    }
};

Live example

The reason you need to do something like this (or what Jarod42 proposed) is that the virtual methods of the base classes are not visible from the derived class when the name is ambiguous. Normally you'd add a using declaration to pull in what you need, but in your case a forwarder might be easier.

like image 196
Daniel Frey Avatar answered Jan 22 '23 18:01

Daniel Frey


You may rewrite MyMessageHandler as follow: Live example

template <typename... Ts> struct MyMessageHandler;

template <typename T> struct MyMessageHandler<T>
{
    virtual void processMsg(const T&) { }
};

template <typename T, typename...Ts>
struct MyMessageHandler<T, Ts...> : MyMessageHandler<T>, MyMessageHandler<Ts...>
{
    using MyMessageHandler<T>::processMsg;
    using MyMessageHandler<Ts...>::processMsg;
};

template <typename... Ts>
struct MyMessageHandler<std::tuple<Ts...>> : public MyMessageHandler<Ts...>
{
};
like image 34
Jarod42 Avatar answered Jan 22 '23 19:01

Jarod42