Blue7
Blue7

Reputation: 2044

c++ Using CRTP to create pure virtual overloads for every type in variaidic template

I want to create a pure virtual interface for every type in a variadic template. For example, a class:

overloads_interface<int,float,bool>

That defines the functions:

virtual void overload(const int& arg) = 0
virtual void overload(const float& arg) = 0
virtual void overload(const bool& arg) = 0

And if I add another type to the varaidic template, e.g

overloads_interface<int,float,bool, std::string>

It will automatically add the overload:

virtual void overload(const std::string& arg) = 0

Then, in order to instantiate this class, I must implement the functions, e.g:

class concrete : public overloads_interface<int,float,bool> {
public:
    virtual void overload(const int& arg) override { /**handle int**/ }
    virtual void overload(const float& arg) override { /**handle float**/ }
    virtual void overload(const bool& arg) override { /**handle bool**/ }
};

Here is my best attempt of creating this class:

template<typename... args>
class overloads_interface {

    using param_pack = std::tuple<args...>;

    static constexpr size_t num_args = sizeof... (args);

    template<size_t i = num_args - 1>
    struct crtp : public crtp<i - 1> {
        using arg = typename std::tuple_element<i, param_pack>::type;
        using super = crtp<i - 1>;

        using super::overload; // unhide overload from super class
        virtual void overload(const arg& arg) = 0;
    };

    template<>
    struct crtp<0> {
        using arg = typename std::tuple_element<0, param_pack>::type;
        virtual void overload(const arg& arg) = 0;
    };

};

But this fails to compile:

/main.cpp:21: error: explicit specialization in non-namespace scope ‘class overloads_interface<args>’
  template<>
           ^
/main.cpp:22: error: template parameters not deducible in partial specialization:
  struct crtp<0> {
         ^~~~~~~

And also has a nested class crtp that I don't really want.

How could I make this class?

Upvotes: 2

Views: 158

Answers (2)

Konstantin Stupnik
Konstantin Stupnik

Reputation: 468

Another way:

template <class T>
struct overloads_helper{
  virtual ~overloads_helper() = default;
  virtual void overload(const T&) = 0;
};

template <class... Args>
class overloads : public overloads_helper<Args>...{
};

Not sure which one is better, inheritance chain or multiple inheritance from interfaces...

Upvotes: 9

Quentin
Quentin

Reputation: 63144

I'm not sure why you're bothering with std::tuple at all, you can simply use partial specialization to unwind the pack:

template<typename... args>
struct overloads;

template<typename head, typename... args>
struct overloads<head, args...> : overloads<args...> {
    using overloads<args...>::overload; // unhide overload from super class
    virtual void overload(const head& arg) = 0;
};

template <typename head>
struct overloads<head> {
    virtual void overload(const head& arg) = 0;
};

See it live on Wandbox

Also note that your crtp is not actually a CRTP, that pattern is about classes of the form class A : Crtp<A> { };.

Upvotes: 5

Related Questions