0

Until 'super' is implemented in c++, I'm looking for a way to emulate it myself. Motivation: Here's a typical scenario:

class A
{
    void SomeMethod();
}

class B : public A
{
    void SomeMethod() override;
}

void B::DoSomething()
{
    A::SomeMethod();
}

All is well, until someone inserts a class in between:

class C : public A
{
    void SomeMethod() override;
}

and changes inheritance:

class B : public C {...}

In most cases I'd like the immediate base class to be called, which is not going to happen unless I explicitly replace all A:: calls with C:: calls.

A 'super' keyword would be of great use here, where it means: "use the immediate base, but issue a compiler error if ambiguous".

Reading some suggestions, I attempted to define as follows:

class A
{
    void SomeMethod();

    protected:
        using super = A;
}

class C
{
    void SomeMethod();

    protected:
        using super = C;
}

void B::DoSomething()
{
    super::SomeMethod();
}

However A::SomeMethod() was called instead of C::SomeMethod()...

How does the compiler treat multiple aliases with the same name?

How can I fix this?

EDIT: the suggested other question is an old one where the solutions might by improved using modern c++.

gil_mo
  • 575
  • 1
  • 6
  • 27
  • 3
    As far as I can tell, `super` is not going to be implemented in C++ because of multiple inheritance. – L. F. Oct 17 '19 at 12:30
  • What compiler/platform are you targeting ? – darune Oct 17 '19 at 12:31
  • Windows and Mac. – gil_mo Oct 17 '19 at 12:32
  • @gil_mo thats a shame - on windows/msvc theres a compiler extrension `__super` – darune Oct 17 '19 at 12:33
  • I would dare say if someone inserts a class in between, then it must somehow be aware of the existing code and practices. If you have a 'using super = C' somewhere, then whoever added that class should be aware of it. You can't prevent stupid. – AlexG Oct 17 '19 at 12:37
  • I'll bet it should be possbile as in the example implementation of https://en.cppreference.com/w/cpp/types/is_base_of but thats too much template magic for me to implement in under 10h. – Superlokkus Oct 17 '19 at 12:37
  • @AlexG it's not about stupidity, it's mainly about having to tediously search & replace all places. – gil_mo Oct 17 '19 at 12:38
  • @gil_mo That you can often avoid by using a `using` or `typedef` alias. – Superlokkus Oct 17 '19 at 12:41
  • The real point is to ask why C++ does not implement `super`. The designer(s) of the language didn't see any gain from adding it. – PaulMcKenzie Oct 17 '19 at 12:45
  • @PaulMcKenzie Since they allow multiple inheritance it was an easy thing to leave out as well. – NathanOliver Oct 17 '19 at 12:48
  • All the 'multiple inheritance' argument is irrelevant. This keyword would be useful 95% of the times, and the compiler would not allow using it when ambiguity pops up. – gil_mo Oct 17 '19 at 12:53
  • 1
    Possible duplicate of [Using "super" in C++](https://stackoverflow.com/questions/180601/using-super-in-c) – Benjamin Bihler Oct 17 '19 at 12:54
  • @BenjaminBihler that's a 2008 post, no modern c++. – gil_mo Oct 17 '19 at 12:59
  • the question is not specifically asking for an old c++ standard, its the answers that might be outdated, but not the question – 463035818_is_not_an_ai Oct 17 '19 at 13:06
  • It's not at all clear that calling `C::SomeMethod()` is the appropriate automatic change when that change in the class hierarchy is made. Class design is **design**; when the class hierarchy changes you have to analyze the effect that those changes have on your code to decide what, if anything, should be done in response. – Pete Becker Oct 17 '19 at 14:33

2 Answers2

3

One way to solve this, but it does limit how you can use the class, is to make it a template. In doing so you can make the base classes a template type and now you have generic names you can refer to to them by. That would look like

struct A
{
    void do_something() { std::cout << "A::do_something\n"; }
};

template <typename Super>
struct B : Super
{
    void do_something() 
    { 
        std::cout << "B::do_something\n"; 
        Super::do_something(); 
    }
};

template <typename Super>
struct C : Super
{
    void do_something() 
    { 
        std::cout << "C::do_something\n"; 
        Super::do_something(); 
    }
};

int main() 
{
    B<A> b;
    b.do_something();
    C<B<A>> c;
    c.do_something();
}

which outputs

B::do_something
A::do_something
C::do_something
B::do_something
A::do_something

You can even user multiple inheritance and access each base individually like

template <typename Super1, typename Super2>
struct B : Super1, Super2
{
    void do_something() 
    { 
        std::cout << "B::do_something\n"; 
        Super1::do_something(); 
        Super2::do_something(); 
    }
};
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • One would probably default the template arguments to whatever the inheritance is supposed to be (so we can write `C<> c;` instead of leaving it up to users to recite the correct inheritance hierarchy every time). That or `using C = CImpl;` after every class. – Max Langhof Oct 17 '19 at 13:03
  • @MaxLanghof I like the using idea better. I didn't want to sway the user one way or the other though so I left that to them to decide how they actually want to use it. – NathanOliver Oct 17 '19 at 13:05
  • I've marked this reply as helpful, however AFAIK it can't serve as the final answer, as it requires large-scale refactoring of my code... – gil_mo Oct 18 '19 at 10:21
0

The requirement doesn't make sense: which base class should be considered the super-class?

If your class has single inheritance, then you can ease the maintenance burden by adding a using declaration:

class B : public A
{
    using Base = A;
    //...
}

Then you have only one line that needs to be changed.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103