David O'Neil Ответов: 5

Название с++ уточняющий вопрос


Действительно ли следующее является ошибкой, как указывает VS? Я думал, что даже без переопределений производный класс может найти имя виртуальной функции, правильно определенной в базовом классе. Да, в обоих классах есть функция с одинаковым именем, но я думал, что поиск имени работает на сигнатуре функции.
#include <iostream>

class BaseItem {
   public:
      virtual std::string str() { return "Hello World"; }
   };

class DerivedItem : public BaseItem {
   private:
      std::string strC;
   public:
      void str(std::string s) { strC = s; }
   };

int main() {
   DerivedItem item;
   std::string str = item.str(); //Error. C2660: func doesn't take 0 args.
   std::string str2 = item.BaseItem::str(); //OK.
   }


Что я уже пробовал:

Запускаю его. Следующая строка работает, но я не вижу реальной причины для прямого указания поиска.

5 Ответов

Рейтинг:
33

CPallini

спецификатор виртуальной функции - cppreference.com[^]:

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

Добавление
virtual std::string str() override { return BaseItem::str(); }
для производного класса ваша программа будет компилироваться.


David O'Neil

Спасибо. Это то, что я искал. Не знал, что поиск имен был ограничен таким образом, так что сегодня вечером узнал кое-что новое!

Jon McKee

Да, это был действительно хороший вопрос. Я тоже кое-чему научился. :)

CPallini

- Итак, сегодня вечером я узнал кое-что новое!"
И я тоже (сегодня утром) :-)

Stefan_Lang

И я тоже - не ожидал узнать что-то настолько простое после десятилетий программирования на C++!

Есть 5.

Jon McKee

EDIT: Nvm, теперь я понимаю, что вы имеете в виду CPallini. Добавляя переопределение в производное, вы позволяете перегрузке правильно происходить снова, поскольку обе функции находятся в одной области видимости.

KarstenK

или он удалит виртуальный квалификатор :-O

David O'Neil

Если бы жизнь была так проста! :)

Garth J Lancaster

+5 Карло :-)

Рейтинг:
2

Rick York

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


David O'Neil

Вы правы, что предоставление ему аргумента дает ошибку по другой причине. Интересно также, что создание виртуальной функции производного класса " str "не устраняет проблему, но дает еще один "не принимает 0 аргументов". Есть только два способа исправить это: 1) как я сделал в исходном примере, 2) создать виртуальную функцию в классе DerivedItem, которая просто возвращает функцию BaseItem. Довольно уродливо. Я думал, что поиск имен C++ был более мощным, чем это, но опять же, я никогда раньше не сталкивался с такой ситуацией.

Рейтинг:
1

Jon McKee

Это... очень интересно. Я не использую C++ тонну, но нашел их:
Поиск неквалифицированного имени - cppreference.com[^]
Поиск, зависящий от аргумента - cppreference.com[^] : Проверьте раздел "подробности".
Доминирование (C++) - Википедия[^]

- Вы правы. Неквалифицированный поиск имен затеняет/скрывает их на основе имен, а не сигнатур, которые включали бы аргументы. Только в тех случаях, когда неквалифицированный поиск не дает никаких результатов, используется поиск, зависящий от аргумента.


David O'Neil

Спасибо! CPallini нашел точную причину, но я сделал это для информации!

Рейтинг:
1

Espen Harlinn

Привет,

Немного опоздал на вечеринку - но вот идет:

Есть простое решение : добавить using BaseItem::str; в публичный раздел DerivedItem, например

class DerivedItem : public BaseItem
{
private:
    std::string strC;
public:
    using BaseItem::str;
    void str( std::string s ) { strC = s; }
};

Это позволит выявить все определения str от BaseItem, независимо от подписи, от DerivedItem.

С уважением
Эспен Харлинн


David O'Neil

Спасибо! Отличное дополнение к разговору!

Рейтинг:
0

Richard MacCutchan

В вашем коде есть две простые ошибки:

std::string str = item.str(); //Error. C2660: func doesn't take 0 args.

1. DerivedItem::str требует строку в качестве своего параметра, вы не можете пропустить это.
2. Он объявлен void поэтому он не возвращает никакого результата.

Измените эти два, и это сработает.


David O'Neil

Вопрос был в том, почему C++ не способен определить, что функция BaseItem 'str' соответствует этому требованию. Я думал, что это должно быть возможно.

Richard MacCutchan

Проблема просто в том, что вы делаете прямой звонок в DerivedItem::str с неправильным синтаксисом. В этот момент нет необходимости вызывать базовый класс, поскольку производная функция переопределяет его. Если бы вы вызывали его через указатель, тогда все могло бы быть по-другому. Существует (не необоснованное) описание в Виртуальные Функции | Microsoft Docs[^].