Что не так с этим наследством? - PullRequest
4 голосов
/ 11 ноября 2008

Я просто не понимаю. Пробовал на VC ++ 2008 и G ++ 4.3.2

#include <map>


class A : public std::multimap<int, bool>
{
public:
    size_type erase(int k, bool v)
    {
        return erase(k); // <- this fails; had to change to __super::erase(k)
    }
};

int main()
{
    A a;
    a.erase(0, false);
    a.erase(0); // <- fails. can't find base class' function?!

    return 0;
}

Ответы [ 9 ]

26 голосов
/ 11 ноября 2008

Когда вы объявляете функцию в классе с тем же именем, но отличающимся от суперкласса, тогда в правилах разрешения имен указывается, что компилятор должен перестать искать функции, которую вы пытаетесь вызвать, как только находит первый матч. После нахождения функции по имени , затем она применяет правила разрешения перегрузки.

Итак, компилятор находит вашу реализацию erase(int, bool) при вызове erase(0), а затем решает, что аргументы не совпадают.

17 голосов
/ 11 ноября 2008

1: Вы должны быть крайне осторожными при выводе из контейнеров стандартной библиотеки C ++. Это можно сделать, но поскольку у них нет виртуальных деструкторов и других подобных тонкостей, обычно это неправильный подход.

2: Правила перегрузки здесь немного странные. Компилятор сначала просматривает производный класс, и если он находит любую перегрузку с тем же именем, он прекращает поиск там. Он просматривает базовый класс только в том случае, если в производном классе не обнаружено перегрузок.

Простым решением этого является введение необходимых функций из базового класса в пространство имен производного класса:

class A : public std::multimap<int, bool>
{
public:
        using std::multimap<int, bool>::erase; // Any erase function found in the base class should be injected into the derived class namespace as well
        size_type erase(int k, bool v)
        {
                return erase(k);
        }
};

В качестве альтернативы, конечно, вы можете просто написать небольшую вспомогательную функцию в производном классе, перенаправив ее на функцию базового класса

9 голосов
/ 11 ноября 2008

Вы скрыли функцию-член стирания базового класса, определив функцию в производном классе с тем же именем, но с разными аргументами.

http://www.parashift.com/c++-faq-lite/strange-inheritance.html#faq-23.9

6 голосов
/ 11 ноября 2008

Прежде всего, вы никогда не должны наследовать от контейнеров STL, потому что ни один контейнер STL не определяет виртуальный деструктор.
Во-вторых, см. Ответ Грега о наследовании.

5 голосов
/ 11 ноября 2008

Подумайте, действительно ли вы хотите наследовать от std :: map. За все время, что я написал код, и это дольше, чем существует STL, я никогда не видел случая, чтобы наследование от std :: container было лучшим решением.

В частности, спросите себя, является ли ваш класс IS мультикартой или HAS мультикартой.

3 голосов
/ 11 ноября 2008

Другие ответили, как решить проблему синтаксиса и почему может быть опасно выводить из стандартных классов, но также стоит указать:

Предпочитают композицию наследованию.

Я сомневаюсь, что вы подразумеваете, что 'A' явно имеет отношение "is-a" к multimap . Стандарты кодирования C ++ Саттера / Александреску имеет целую главу по этому вопросу (# 34), и Google указывает на множество хороших ссылок на эту тему.

Похоже, что по этой теме тоже есть тема .

1 голос
/ 13 ноября 2008

Я согласен с комментариями других, что вы должны быть очень осторожны, унаследуя от классов STL, и этого почти всегда следует избегать.

Однако эта проблема может возникнуть с другим базовым классом, от которого вполне уместно наследовать.

Мой вопрос: почему бы не дать вашей функции с двумя аргументами другое имя? Если он принимает разные аргументы, предположительно, он имеет немного другое значение? Например. erase_if_true или erase_and_delete или все, что означает bool.

1 голос
/ 11 ноября 2008

Для тех, кто использует Effective C ++ в качестве справочника по программированию на C ++, эта проблема рассматривается в пункте 33 (Избегайте сокрытия унаследованных имен.) В книге.

0 голосов
/ 11 ноября 2008

Чтобы заменить __super переносимым способом, определите typedef в верхней части вашего класса следующим образом:

typedef std::multimap<int, bool> parent;
public:
    size_type erase(int k, bool v)
    {
            return parent::erase(k);
    }

Конечно, не обязательно быть «родителем». Это может быть любое имя, которое вам нравится, если оно постоянно используется в вашем проекте.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...