'using' keyword to choose from multiple virtual inherited functions
Asked Answered
H

2

11

I have a class ('TestC'), which is derived from two other classes ('TestA' and 'TestB'), both of which have a virtual function with the same signature.

To make the function accessible through 'TestC', I have to tell it which version to use. This works if I explicitly overwrite the function in 'TestC' and call the version I want:

#include <iostream>

class TestA
{
public:
    virtual void test() {std::cout<<"a";}
};

class TestB
{
public:
    virtual void test() {std::cout<<"b";}
};

class TestC
    : public TestA,public TestB
{
public:
    void test() {TestB::test();}
};

int main(int argc,char *argv[])
{
    TestC c;
    TestA *a = static_cast<TestA*>(&c);
    a->test();
    c.test();
    for(;;);
    return EXIT_SUCCESS;
}

Output: "bb"

That's the expected result. However, I noticed that if I use the 'using' keyword, instead of overwriting the function explicitly, I get some unexpected behavior:

class TestC
    : public TestA,public TestB
{
public:
    using TestB::test;
};

(Everything else is the same)

Output: "ab"

Can someone explain this to me? It looks like 'test' is suddenly not virtual anymore? Is there any way to do this, without explicitly overwriting the function? (Something like "using override")

Hypethral answered 3/6, 2016 at 9:49 Comment(0)
U
4

A using-declaration does never override a virtual function. However, it can disambiguate multiple inheritance. In the examples a->test() invokes:

  1. the override of TestC (which invokes TestB::f)
  2. the override of TestB (TestC has no override)

The using-declaration in the following (diamond shaped) virtual inheritance does not provide the required final override:

struct Base {
    virtual void f();
};
struct A : virtual Base {
    virtual void f() override {}
};
struct B : virtual Base {
    virtual void f() override {}
};

struct Derived : A, B {
    // error: no unique final overrider for ‘virtual void Base::f()’ in ‘Derived’
    using A::f;
};

More examples:

#include <iostream>

// Single Inheritance
// ==================

namespace Single {

struct A {
    virtual void f() { std::cout<<"A\n"; }
};
struct AA : A {
    virtual void f() override { std::cout<<"AA\n"; }
};

struct Derived : AA {
    // Has no impact if not called explicitly
    using A::f;
};

} // namspace


// Multiple Inheritance
// ====================

namespace Multiple {

struct A {
    virtual void f() { std::cout<<"A\n"; }
};
struct AA : A {
    virtual void f() override { std::cout<<"AA\n"; }
};
struct B {
    virtual void f() { std::cout<<"B\n"; }
};
struct BB : B {
    virtual void f() override { std::cout<<"BB\n"; }
};
struct Derived : AA, BB {
    // Disambiguate A::f (AA::f) and B::f (BB::f)
    using A::f;
};

} // namspace


// Test
// ====

int main() {
    std::cout << "Single\n";
    {
        Single::Derived d;
        d.f();          // calls AA::f
        d.Derived::f(); // calls A::f because of the using-declaration
    }

    std::cout << "Multiple\n";
    {
        Multiple::Derived d;
        d.f();          // calls AA::f
        d.Derived::f(); // calls A::f because of the using-declaration
    }
}

Note: I have not found any conclusive explanation in the draft standard N4296.

Uplift answered 3/6, 2016 at 21:5 Comment(0)
M
11

With using, you don't define TestC::test(), but tell that TestB::test() is visible (and so hides TestA::test() which makes c.test() not ambiguous).

Mikemikel answered 3/6, 2016 at 9:57 Comment(0)
U
4

A using-declaration does never override a virtual function. However, it can disambiguate multiple inheritance. In the examples a->test() invokes:

  1. the override of TestC (which invokes TestB::f)
  2. the override of TestB (TestC has no override)

The using-declaration in the following (diamond shaped) virtual inheritance does not provide the required final override:

struct Base {
    virtual void f();
};
struct A : virtual Base {
    virtual void f() override {}
};
struct B : virtual Base {
    virtual void f() override {}
};

struct Derived : A, B {
    // error: no unique final overrider for ‘virtual void Base::f()’ in ‘Derived’
    using A::f;
};

More examples:

#include <iostream>

// Single Inheritance
// ==================

namespace Single {

struct A {
    virtual void f() { std::cout<<"A\n"; }
};
struct AA : A {
    virtual void f() override { std::cout<<"AA\n"; }
};

struct Derived : AA {
    // Has no impact if not called explicitly
    using A::f;
};

} // namspace


// Multiple Inheritance
// ====================

namespace Multiple {

struct A {
    virtual void f() { std::cout<<"A\n"; }
};
struct AA : A {
    virtual void f() override { std::cout<<"AA\n"; }
};
struct B {
    virtual void f() { std::cout<<"B\n"; }
};
struct BB : B {
    virtual void f() override { std::cout<<"BB\n"; }
};
struct Derived : AA, BB {
    // Disambiguate A::f (AA::f) and B::f (BB::f)
    using A::f;
};

} // namspace


// Test
// ====

int main() {
    std::cout << "Single\n";
    {
        Single::Derived d;
        d.f();          // calls AA::f
        d.Derived::f(); // calls A::f because of the using-declaration
    }

    std::cout << "Multiple\n";
    {
        Multiple::Derived d;
        d.f();          // calls AA::f
        d.Derived::f(); // calls A::f because of the using-declaration
    }
}

Note: I have not found any conclusive explanation in the draft standard N4296.

Uplift answered 3/6, 2016 at 21:5 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.