Страницы

Поиск по вопросам

вторник, 28 января 2020 г.

Почему ОТКРЫТАЯ функция из родительского класса становится недоступной в ОТКРЫТО унаследованном классе

#cpp #qt #gcc #компилятор #стандарт


если в производном классе имеется функция с тем же именем, но различной сигнатурой?

Пример.
Создаем класс TwoStageMap, открыто унаследованного от QMap:

template>
class TwoStageMap : public QMap
{
public:
    void insert(const F &fkey, const S &skey, const T &value1); //TODO: return an
iterator of what?

    void insert(const QPair &pair, const T &value1);

    T value(const F &fkey, const S &skey);

    T value(const QPair &pair);

};


При попытки обратиться к value() с сигнатурой из производного класса (TwoStageMap)
все прекрасно работает:

TwoStageMap> tsMap;

tsMap.insert(31, "October", "Halloween");
tsMap.insert(31, "December", "New Year's Eve");
tsMap.insert(25, "December", "Xmas");
tsMap.insert({25, "October"}, "Canna");

qDebug() << "Day1:" << tsMap.value1(31, "December");


, но как только попытаемся вызвать value() самого QMap (чтобы получить внутренний
контейнер под первым ключем), tsMap.value(31);, то тут же получаем ошибку:


  no matching member function for call to 'value'


При переименовании функции, например, в value1() проблема исчезает.
Вопрос - это ошибка компилятора или стандарт языка?
Компирятор gcc x86 64bit



Реализация класса, если кто хочет его использовать/попробовать:

template
void TwoStageMap::insert(const F &fkey, const S &skey, const T &value)
{
    Cont innerMap;
        if (QMap::contains(fkey)) {
            innerMap = QMap::value(fkey);
        }

    innerMap.insert(skey, value);
    QMap::insert(fkey, innerMap);

    //TODO: return an iterator of what?
}

template
void TwoStageMap::insert(const QPair &pair, const T &value)
{
    return insert(pair.first, pair.second, value);
}

template
T TwoStageMap::value(const F &fkey, const S &skey)
{
    auto innerMap = QMap::value(fkey);
    return innerMap.value(skey);
}

template
T TwoStageMap::value(const QPair &pair)
{
    return value1(pair.first, pair.second);
}




PS 
В примере Вызов функции_члена шаблонного базового класса из функции производного
шаблонного класса ситуация с невидимостью неквалифицированного имени функции шаблонного
родительского класса, с этим, как раз, вопросов нет, у меня, как можно видеть, идет
обращение через QMap::, здесь же немного другой случай, а именно, вопрос в
том, что мешает компилятору распознать перегружанную функцию с другой сигнатурой, используя
т.н. "искажение имен"? Если это стандарт языка, то вопрос, скорее к Комитету
    


Ответы

Ответ 1



При вызове метода класса по имени без квалификатора tsMap.value(31); для построения списка перегрузок будет осуществлен поиск имени без квалификатора в области видимости класса TwoStageMap согласно спецификации обращения к членам класса: 6.4.5 Class member access [basic.lookup.classref] 2 If the id-expression in a class member access (8.5.1.5) is an unqualified-id, and the type of the object expression is of a class type C , the unqualified-id is looked up in the scope of class C. Этот поиск будет завершен при нахождении имени value в классе TwoStageMap, так как поиск имени без квалификатора должен завершаться сразу при нахождении первого объявления, согласно спецификации поиска имени без квалификатора: 6.4.1 Unqualified name lookup [basic.lookup.unqual] 1 In all the cases listed in 6.4.1, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. Таким образом метод value из базового класса QMap в списке перегрузок будет отсутствовать. Чтобы это исправить можно Внести этот метод в область видимости класса TwoStageMap добавив using QMap::value; При вызове использовать имя с квалификатором: tsMap.QMap::value(31);

Ответ 2



Если я правильно понял проблему, то решается эта проблема с помощью using. #include #include class Base { public: void func(int _v) { std::cout << __FUNCTION__ << std::endl; } }; class Derived : public Base { public: using Base::func; void func(const std::string &_str) { std::cout << __FUNCTION__ << std::endl; } }; int main(int argc, char *argv[]) { Derived d; d.func("text"); d.func(1); return 0; }

Ответ 3



Не важно какой класс вы напишете. Допустим вы написали такой простой класс: class Your_class { protected: int n{ 3 }; public: int value(int n) const { return n + 3; } }; Если я наследую ваш класс, то я наследую все, что не является закрытым членами вашего класса: class My_class : public Your_class { }; В таком виде мой класс имеет функцию_член и обьект n вашего класса. Но как только я добавлю в класс: class My_class : public Your_class { public: bool value(const std::string& s) { return n == s.length(); } }; Этим я выражаю, что моя value это совсем другая функция(мне не нужен ваш вариант). И экземпляры моего класса не будут иметь возможность пользоваться одноименной функцией базового вашего класса. Для обеспечения этой возможности я смогу написать другую функцию, которая вызовит value вашего класса, или же сделать так, как описан в другом ответе. Точно также, если я добавляю в класс свой обьект n, то экземпляры моего класса будут пользоваться только этим обьектом. Так что, функция=член в производном классе с таким же именем, что и функция_ член в базовом, не является ее перегрузкой, а является ее заменой

Комментариев нет:

Отправить комментарий