3

I have a system with a number of message types that can be published/subscribed. When a new type is added, a large number of files have to be updated to add support for the new method type.

Is it possible to declare a C++ class with overloaded pure virtual methods as a variadic data structure?

When the C++ program below is compiled, the declaration of the 2nd publish method for type "MessageB" is hidden by the declaration of "MessageA".

main.cpp:57:41: error: no matching function for call to ‘IPublisher::publish(MessageB)’ publisher->publish(MessageB{"world"});

This is consistent with both GCC and Clang, but Clang does emit the useful warning:

warning: 'IPublishOne::publish' hides overloaded virtual function [-Woverloaded-virtual] virtual void publish(const T& message) = 0;

Why would method hiding occur since MessageA and MessageB are distinct types?

#include <iostream>
#include <memory>

using namespace std;

template <class...Ts>
struct IPublishOne;

template <class T>
struct IPublishOne<T>
{
    virtual void publish(const T& message) = 0;
};

template <class T, class... Ts>
struct IPublishOne<T, Ts...> : IPublishOne<Ts...>
{
    virtual void publish(const T& message) = 0;
};

struct MessageA
{
    std::string value;
};

struct MessageB
{
    std::string value;
};

struct IPublisher : public IPublishOne<MessageA, MessageB>
{
    virtual ~IPublisher() = default;
};

struct Publisher : public IPublisher
{

    void publish(const MessageA& message) override
    {
        std::cout << message.value << std::endl;
    }

    void publish(const MessageB& message) override
    {
        std::cout << message.value << std::endl;
    }
};

int main()
{
    const std::unique_ptr<IPublisher> publisher = std::make_unique<Publisher>();

    publisher->publish(MessageA{"hello"});

    // this produces compile error
    publisher->publish(MessageB{"world"});

    return 0;
}
Adam Crain
  • 53
  • 4

2 Answers2

3

Why would method hiding occur since MessageA and MessageB are distinct types?

You lack the final overrider.

IPublishOne<T, Ts...>::publish overrides IPublishOne<T>::publish as per class.virtual/2:

If a virtual member function vf is declared in a class Base and in a class Derived, derived directly or indirectly from Base, a member function vf with the same name, parameter-type-list, cv-qualification, and ref-qualifier (or absence of same) as Base​::​vf is declared, then Derived​::​vf is also virtual (whether or not it is so declared) and it overrides Base​::​vf.

What you need is a final overrider:

Specify the final overrider in struct IPublishOne<T, Ts...>:

template <class T, class... Ts>
struct IPublishOne<T, Ts...> : IPublishOne<Ts...>
{
    using IPublishOne<Ts...>::publish;  // final overrider for
                                        // IPublishOne<T>::publish
    virtual void publish(const T& message) = 0;
};
Joseph D.
  • 11,804
  • 3
  • 34
  • 67
1

The problem is that defining a publish() method in a class hide all the publish() methods inherited from the base classes.

You can solve this problem adding a using in IPublishOne

template <class T, class... Ts>
struct IPublishOne<T, Ts...> : IPublishOne<Ts...>
{
   using IPublishOne<Ts...>::publish;  // <<<-------------

    virtual void publish(const T& message) = 0;
};

to give visibility to the inherited publish().

max66
  • 65,235
  • 10
  • 71
  • 111