简体   繁体   中英

Static inheritance with variadic templates

I am trying to use std::conditional to implement static inheritance. I have two possible parents of my child class, parent_one , which should hold multiple variables based on passed types, and parent_two , which takes two types. I am using tag dispatching to differ between the classes I want to inherit from.

Now to the problem. When I am calling child and tagging it to inherit from parent_one with two types, it works as intended. However, if I try to pass any number of types into the child with the intention of inheriting from parent_one , I get error:

static_polymorphism.cpp: In instantiation of ‘class child<foo_type, int, int, double, float>’:
static_polymorphism.cpp:110:41:   required from here
static_polymorphism.cpp:99:7: error: wrong number of template arguments (4, should be 2)
   99 | class child : public std::conditional_t<
      |       ^~~~~
static_polymorphism.cpp:90:7: note: provided for ‘template<class T, class F> class parent_two’
   90 | class parent_two {
      |       ^~~~~~~~~~
static_polymorphism.cpp: In function ‘int main(int, char**)’:
static_polymorphism.cpp:111:9: error: ‘class child<foo_type, int, int, double, float>’ has no member named ‘log’
  111 |   first.log();

If I understand that correctly the compiler should generate code based on my tag dispatching. That means it should create overloaded classes - N(Based on types passed) from parent_one and M(Based on types passed as well) from parent_two . However for some reason, unknown to me, it is not accepting the variable count of types. Could you tell me what am I doing wrong please?

Implementation is here.

using one_t = struct foo_type{};
using two_t = struct bar_type{};

template <typename ... TYPES>
class parent_one {
public:
  parent_one() = default;
  void log() {
    std::cout << "parent_one" << std::endl;
  }
};

template <typename T, typename F>
class parent_two {
public:
  parent_two() = default;
  void log() {
    std::cout << "parent_two" << std::endl;
  }
};

template <typename T, typename ... ARGS>
class child : public std::conditional_t<
    std::is_same_v<T, one_t>,
    parent_one<ARGS...>,
    parent_two<ARGS...>
  >
{
public:
  child() = default;
};

int main(int argc, char *argv[]) {
  child<one_t, int, int, double, float> first;
  first.log();

  child<two_t, int, int> second;
  second.log();
  return 0;
}

std::conditional_t<
    std::is_same_v<T, one_t>,
    parent_one<ARGS...>,
    parent_two<ARGS...>
>

Here both alternatives are validated before the condition is checked. std::conditional_t is not magical, being just a regular template it requires all template arguments to be valid before it can do anything.

You need to delay the substitution of template arguments into a parent template until after one of the alternatives is selected. Here's one possible solution:

template <template <typename...> typename T>
struct delay
{
    template <typename ...P>
    using type = T<P...>;
};
// ...
class child :
    public std::conditional_t<
        std::is_same_v<T, one_t>,
        delay<parent_one>,
        delay<parent_two>
    >::template type<ARGS...> 
{
    // ...
};

You may be classic:

template<class T, class... Args> struct child: parent_one<Args...> {};

template<class T, class A, class B> struct child<T, A, B>: parent_two<A, B> {};

template<class A, class B> struct child<one_t, A, B>: parent_one<A, B> {};

(the two specializations can be combined into one with requires (!std::is_same_v<T, one_t>) ).

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM