5

Consider I have two pure virtual classes, one deriving from the another and a concrete class deriving from the last mentioned:

#include <iostream>
#include <string>

class Abstract1
{
public:
    virtual ~Abstract1() { };
    virtual void method(int a) = 0;

protected:
    Abstract1() = default;
};

class Abstract2: public Abstract1
{
public:
    virtual ~Abstract2() { };
    virtual void method(char c, std::string s) = 0;

protected:
    Abstract2() = default;
};

class Concrete : public Abstract2
{
public:
    void method(int a) override
    {
        std::cout << __PRETTY_FUNCTION__ << "a: " << a << std::endl;
    }

    void method(char c, std::string s) override
    {
        std::cout << __PRETTY_FUNCTION__ << "c: " << c << "; s: " << s << std::endl;
    }
};

When I create a pointer of the type Abstract2* I can't have access to the override method from Abstract1.

int main()
{
    Concrete c;
    c.method(42);
    c.method('a', std::string("string"));

    Abstract2 *ptr_a2 = &c;
    ptr_a2->method(13); //Error
    ptr_a2->method('b', std::string("string2"));
}

I got the following error, saying that the only existing method is the Absctract2 overloaded:

<source>: In function 'int main()':
<source>:49:22: error: no matching function for call to 'Abstract2::method(int)'

   49 |     ptr_a2->method(13);

      |                      ^

<source>:22:18: note: candidate: 'virtual void Abstract2::method(char, std::string)'

   22 |     virtual void method(char c, std::string s) = 0;

      |                  ^~~~~~

<source>:22:18: note:   candidate expects 2 arguments, 1 provided

Does anybody know why does it happen or how to fix it? I mean, the only reasonable solution I got was to add

virtual void method(int a) override = 0;

inside Abstract2 class.

Is it a case of "name hiding"? Why only on pointer and not on the Concrete class then? The number of parameters are different is not only a close-type thing.

Here's a link to play with it online where the example was created: https://godbolt.org/z/gxKpzN

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
Paiusco
  • 305
  • 1
  • 14
  • 3
    Interesting that it'll work (https://ideone.com/CojHlB) if you change the name in `Abstract1`. This is definitely something to do with the overloading. Also kudos for a solid short, reproducible example and well-written question. I hope we see more from you! – scohe001 Jan 15 '20 at 17:37
  • 5
    Yes this is [name hiding](https://stackoverflow.com/questions/37861389/why-c-does-not-support-overloading-across-scopes/37861586#37861586). `Abstract2::method` hides `Abstract1::method`. However, both `method` are visible in `Concrete`, which is why you can call both of them from `Concrete`. If you want `Abstract1::method` to be visible from `Abstract2` you can do a `using Abstract1::method;` inside the definition of `Abstract2`. – Raymond Chen Jan 15 '20 at 17:38
  • This makes me wonder... By issuing a `using Abstract1::method` you can "import" all overloads of method into `Abstract2`, but is there a way to import a specific one? – j4x Jan 15 '20 at 17:41
  • 1
    @Ray imho this question deserves an answer, not just a comment ;) – 463035818_is_not_an_ai Jan 15 '20 at 17:47
  • @j4x, that's worth a question of its own, if it hasn't already been asked. – Toby Speight Jan 15 '20 at 18:07
  • 1
    `static_cast(ptr_a2)->method(13);` – RbMm Jan 15 '20 at 18:45
  • 1
    OK @TobySpeight. I posted it: https://stackoverflow.com/questions/59758453/how-to-make-all-hidden-names-from-a-base-class-accessible-in-derived-one – j4x Jan 15 '20 at 19:38

1 Answers1

4

Yes, the name is hidden in Abstract2 (but visible again in Concrete, where the override is declared). The easiest way to make it accessible in Abstract2 is to add a using statement:

class Abstract2: public Abstract1
{
public:
    using Abstract1::method;
    virtual void method(char c, std::string s) = 0;
};

Note that it's not relevant whether the member function is pure virtual or has a definition, and we see the same whether looking at a concrete Abstract2 or a reference or pointer to type Abstract2.

Toby Speight
  • 27,591
  • 48
  • 66
  • 103
  • 1
    Sure but why only on pointers? Why doesn't it happen on any case? Using the Concrete object I can access both methods. – Paiusco Jan 15 '20 at 18:22
  • 1
    Yes, both methods are visible when you have a `Concrete`, but when you have a `Abstract2`, then `Abstract1::method` is hidden by `Abstract2::method`. The only reason that we're using pointers or references is that `Abstract2` is an abstract class, so we can't have an instance of it. If you change the pure abstract member functions (replace `= 0;` with `{}`), then you can instantiate an `Abstract2` and demonstrate the issue there, too. – Toby Speight Jan 15 '20 at 18:29
  • 1
    `void test(Abstract2& a2) { a2.method(1); }` demonstrates that it's hidden even without using pointers. The point is that you have an `Abstract2`, and with `Abstract2` the only visible `method` is the two-parameter one. – Raymond Chen Jan 15 '20 at 20:23