Указатель, полученный из чистого виртуального класса (A), не может получить доступ к методу перегрузки из чистого класса (B) - PullRequest
5 голосов
/ 15 января 2020

Учтите, у меня есть два чистых виртуальных класса, один из которых является производным от другого, а конкретный класс - из последнего упомянутого:

#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;
    }
};

Когда я создаю указатель типа Abstract2*, я не могу иметь доступ к методу переопределения с 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"));
}

Я получил следующую ошибку, говоря, что единственный существующий метод - это Absctract2 перегружен:

<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

Кто-нибудь знает, почему это происходит или как это исправить? Я имею в виду, что единственное разумное решение, которое я получил, это добавить

virtual void method(int a) override = 0;

внутри Abstract2 класса.

Это случай "скрытия имени"? Почему тогда только по указателю, а не по классу Concrete? Количество параметров различается, это не только вещь близкого типа.

Вот ссылка, чтобы поиграть с ней онлайн, где был создан пример: https://godbolt.org/z/gxKpzN

1 Ответ

4 голосов
/ 15 января 2020

Да, имя скрыто в Abstract2 (но снова отображается в Concrete, где объявляется переопределение). Самый простой способ сделать его доступным в Abstract2 - добавить оператор using:

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

Обратите внимание, что неважно, является ли функция-член чисто виртуальной или имеет определение, и мы видим То же самое, если смотреть на конкретный Abstract2 или ссылку или указатель на тип Abstract2.

...