16

The derived class hides the name of an overload set from the base class if the derived class has the same name defined, but we can always introduce that overload set back with using-declaration:

template <class BASE>
class A : public BASE
{
public:
  using BASE::some_method;
  void some_method();
}

But what if I introduce all overload sets from variadic base classes? Would I be able to write something like this?

template <class... BASES>
class A : public BASES...
{
public:
  using BASES::some_method...;
  void some_method();
}

I've considered using a helper class like:

template <class... BASES>
struct helper;

template <>
struct helper<> {};

template <class OnlyBase>
struct helper<OnlyBase> : OnlyBase
{
  using OnlyBase::some_method;
};

template <class Base1, class... OtherBases>
struct helper<Base1, OtherBases> : public Base1, public helper<OtherBases...>
{
  using Base1::some_method;
  using helper<OtherBases...>::some_method;
};

And it does work. But it requires a lot of typing (of course I can use macro but I try to use c++'s compile-time feature whenever possible), and when I want to introduce more methods, i have to change much in that piece of code.

A perfect answer would be a simple syntax, but if there's none, I will go with the helper class.

Single34
  • 161
  • 2
  • 1
    Recursive inheritance, or ADL galore (the latter requires modification to all the bases). C++17 might allow pack expansion in using declarations - there's an open EWG issue about this. – T.C. Nov 28 '14 at 07:15
  • @T.C. I wrote a paper: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0195r0.html – Columbo Feb 18 '16 at 21:24

1 Answers1

7

Here is a trick how to reduce handwriting:

// U<X,Y> is a binary operation on two classes
template<template<class,class>class U, class... Xs> struct foldr;
template<template<class,class>class U, class X> struct foldr<U,X> : X {};
template<template<class,class>class U, class X, class... Xs> struct foldr<U,X,Xs...> : U<X, foldr<U,Xs...>> {};

// our operation inherits from both classes and declares using the member f of them    
template<class X, class Y> struct using_f : X,Y { using X::f; using Y::f; };

struct A { void f(int) {} };
struct B { void f(char) {} };
struct C { void f(long) {} };

struct D : foldr<using_f, A, B, C> {};


int main() {
    D d;
    d.f(1);
    d.f('1');
    d.f(1L);
    return 0;
}

So we should write foldr once, then write simple ad-hoc operations - using_f, using_g, using_f_g

Maybe there is a way to further simplifying. Let me think a bit...

Nickolay Merkin
  • 2,673
  • 1
  • 16
  • 14
  • This is a working code that can be used as is for very simple cases like on your example. However, it is unsuitable as a generic solution since base classes can have user-defined constructors. It would be nice if you have provided a case for particular example where base classes have custom constructors (accepting `int`, `char` or `long`). In C++17 it is very easy. – Sergey Kolesnik Jan 22 '21 at 13:21
  • Well, I wrote that answer in 2015, before C++17 :) AFAIK, C++17 allows `using BASES::some_method ...` pack expansion. – Nickolay Merkin Jan 24 '21 at 16:40
  • On constructors. I can't catch what case do you mean? Most general way to construct a compound object is to copy/move its bases. Something like that: ` template class Obj : public Bases... { public: explicit Obj(Bases&& ...bases) : Bases(std::move(bases))... {} ...... }; Obj o(A{1,2,3}, B(4, 5), C()); ` – Nickolay Merkin Jan 24 '21 at 16:46
  • I have references inside base classes, so they need to be initialized. Now I use const pointers and an ugly assignment with const cast... – Sergey Kolesnik Jan 24 '21 at 16:49
  • Still can't catch. – Nickolay Merkin Feb 05 '21 at 10:02