Форум программистов «Весельчак У»
  *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

  • Рекомендуем проверить настройки временной зоны в вашем профиле (страница "Внешний вид форума", пункт "Часовой пояс:").
  • У нас больше нет рассылок. Если вам приходят письма от наших бывших рассылок mail.ru и subscribe.ru, то знайте, что это не мы рассылаем.
   Начало  
Наши сайты
Помощь Поиск Календарь Почта Войти Регистрация  
 
Страниц: [1]   Вниз
  Печать  
Автор Тема: Перегрузка функций  (Прочитано 16481 раз)
0 Пользователей и 2 Гостей смотрят эту тему.
Finch
Спокойный
Администратор

il
Online Online
Пол: Мужской
Пролетал мимо


« : 28-11-2015 22:33 » 

Имеем такой тестовый код
Код: (C++)
#include <iostream>

class A {
public:
  A() {
  }
  virtual ~A() {
  }

  void print(int a) {
    std::cout << a << std::endl;
  }

  virtual void print() {
    std::cout << "Class A" << std::endl;
  }
};

class B : public A{
public:
  B():A() {
  }

  ~B() {
  }
 
  virtual void print() {
    A::print();
    std::cout << "Class B" << std::endl;
  }
};

int main() {
  B b;
  b.print();
  b.print(42);
  return 0;
}
Компилирую в GCC
g++ parent.cpp -o parent
parent.cpp: In function ‘int main()’:
parent.cpp:36:13: error: no matching function for call to ‘B::print(int)’
   b.print(42);
             ^
parent.cpp:36:13: note: candidate is:
parent.cpp:27:16: note: virtual void B::print()
   virtual void print() {
                ^
parent.cpp:27:16: note:   candidate expects 0 arguments, 1 provided
Т.е. Компилятор перестает видеть другой вариант функции в классе A. Обозначение его виртуальным в классе A не помогает. Если его обозначить в классе B
Код: (C++)
  virtual void print(int a) {
    A::print(a);
  }
Решает проблему компиляции. Собственно вопрос, Это бага или фича компилятора? Мне бы не хотелось таскать из класса в класс определение.
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Finch
Спокойный
Администратор

il
Online Online
Пол: Мужской
Пролетал мимо


« Ответ #1 : 28-11-2015 22:51 » 

PS. Кстати добавление в дочерний класс строки   using A::print; тоже решает проблему компиляции. Хотя также лишние шевеления ручками.
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #2 : 29-11-2015 06:10 » 

Собственно вопрос, Это бага или фича компилятора?

Ни то, ни другое. Компилятор ни при чем, это фича (или бага, как пожелаете) самого языка C++. С другим компилятором должно быть то же самое, если он "правильный".

PS. Кстати добавление в дочерний класс строки   using A::print; тоже решает проблему компиляции. Хотя также лишние шевеления ручками.

Отнюдь не лишние - вынужденные. Следствие сокрытия имен: поскольку класс B определяет область видимости и в этой области определено имя print, поиск за ее пределами не производится. Использование using корректно решает эту проблему.

Если интересны детали, они описаны много где, например, в книге Скотта Мэйерса "Эффективное использование C++", гл. 6, правило 33.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Finch
Спокойный
Администратор

il
Online Online
Пол: Мужской
Пролетал мимо


« Ответ #3 : 29-11-2015 17:02 » new

Понятно. Получается, что наследование и перегрузка это две несовместимые веши. Вернее слишком много нужно телодвижений.
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
RXL
Технический
Администратор

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #4 : 29-11-2015 17:09 » 

Вить, тоже порекомендую эту книгу. Читал "как детектив".

Кстати, есть новая книга Майерса о C++11 и C++14, но пока только на английском.
« Последнее редактирование: 29-11-2015 17:13 от RXL » Записан

... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #5 : 29-11-2015 18:19 » 

Понятно. Получается, что наследование и перегрузка это две несовместимые веши.

Ну не то чтобы совсем уж несовместимые. Да и C++, мягко говоря, не самый объектно-ориентированный язык, чтобы все в нем было чисто и гладко. Скорее эксперимент, на ошибках которого развивались более поздние языки.

Вернее слишком много нужно телодвижений.

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

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Finch
Спокойный
Администратор

il
Online Online
Пол: Мужской
Пролетал мимо


« Ответ #6 : 29-11-2015 19:02 » 

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

Если например, тот же самый пример переписать так:
Код: (C++)
int main() {
  A *b=new B();
  b->print();
  b->print(42);
  return 0;
}
То все прекрасно компилируется и видится.

Ну собственно, чтобы не заморачиваться постоянно с перегрузкой. Я просто от нее отказался.
« Последнее редактирование: 29-11-2015 19:13 от Finch » Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #7 : 29-11-2015 19:48 » 

Если например, тот же самый пример переписать так:
...
То все прекрасно компилируется и видится.

Само собой. У Вас ведь b - указатель на экземпляр типа A, поэтому компилятор без труда находит правильную сигнатуру метода в данной области видимости.

Ну собственно, чтобы не заморачиваться постоянно с перегрузкой. Я просто от нее отказался.

Может, оно и к лучшему. Если в проекте участвуют несколько человек и нагорожена иерархия классов с переопределенными, да еще и перегруженными методами, легко запутаться. Не забываем, что C++ не является строго типизированным языком, поэтому возможны очень (да и не очень) тонкие сюрпризы, которые потребуют не один час для выяснения их причины. Хорошо, если она на поверхности, как в данном случае.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #8 : 30-11-2015 08:03 » 

Показал на работе дважды плюсанутым коллегам. Тема вызвала довольно оживленный интерес. Никто не ожидал такого коварства от столь, казалось бы, простого примера. Просили передать топикстартеру большое спасибо за науку.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Finch
Спокойный
Администратор

il
Online Online
Пол: Мужской
Пролетал мимо


« Ответ #9 : 30-11-2015 16:28 » 

Улыбаюсь Яшик пива хоть выиграл?
Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #10 : 30-11-2015 19:40 » 

Да я и не спорил с ними. Помню завет моего научного руководителя в Универе, который говорил: в каждом споре есть два участника - дурак и мерзавец. Дурак не знает и спорит, мерзавец знает и спорит. Не хотелось быть мерзавцем.

Просто сказал им, что с сегодняшнего дня в стандарт C++ внесено изменение: отменено наследование как сложная в изучении и ненадежная в использовании фича, и компиляторы были автоматически пропатчены для ее исключения. В доказательство того, что методы базового класса больше не наследуются дочерними, привел фрагмент кода из топика.

Насладился реакцией, потом сжалился и рассказал, как оно устроено на самом деле. Нет бы до 1 апреля приберечь...

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

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Finch
Спокойный
Администратор

il
Online Online
Пол: Мужской
Пролетал мимо


« Ответ #11 : 30-11-2015 19:59 » 

В Шарпе кстати тоже есть приколы. Я когда рылся в нете в поисках ответа, находил примеры неоднозначности шарпа Улыбаюсь
Простой пример. В классе A определен
public void print(int)
В дочернем классе B определен
public void print(Object)

Вопрос, Какой именно метод выбирет вызов B.print(42)? На Хабре, в статье было написано, что print(Object)
Так как 42 можно косвенно преобразовать в Object. И дальше компилятор не смотрит.

PS. Вот собствеено линк на статью http://habrahabr.ru/post/149287/ Сейчас просмотрел, еше более печально, чем даже я описал.
« Последнее редактирование: 30-11-2015 20:16 от Finch » Записан

Не будите спашяго дракона.
             Джаффар (Коша)
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #12 : 01-12-2015 03:44 » 

Я когда рылся в нете в поисках ответа, находил примеры неоднозначности шарпа Улыбаюсь

Это скорее не неоднозначность, а отличие действительной реализации от ожидаемой при неглубоком проникновении в предмет. Неоднозначностью я бы назвал заранее оговоренное стандартом неопределенное поведение вроде некоторых моментов C и C++, которые зависят от конкретного компилятора и/или его опций.

Мне данное решение разработчиков представляется вполне логичным. Не худшее решение проблемы хрупкости иерархии наследования.

Вообще мое глубокое IMHO: использование аргумента object для одного из вариантов перегрузки без ну очень веской на то причины - однозначный признак говнокодинга, наподобие глобальных переменных или goto. Используя аргумент object, автор тем самым говорит: я создал вундервафлю, способную корректно обработать вообще любой объект, каким бы он ни был. Немудрено, если компилятор ловит его на слове и вызывает всеядный метод, когда сочтет нужным. Ответ на провокацию. Поэтому пример на Хабре меня не слишком расстроил.

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

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Sla
Команда клуба

ua
Offline Offline
Пол: Мужской

WWW
« Ответ #13 : 01-12-2015 08:30 » 

В Шарпе кстати тоже есть приколы. Я когда рылся в нете в поисках ответа, находил примеры неоднозначности шарпа Улыбаюсь
Простой пример. В классе A определен
public void print(int)
В дочернем классе B определен
public void print(Object)

Вопрос, Какой именно метод выбирет вызов B.print(42)? На Хабре, в статье было написано, что print(Object)
Так как 42 можно косвенно преобразовать в Object. И дальше компилятор не смотрит.

PS. Вот собствеено линк на статью http://habrahabr.ru/post/149287/ Сейчас просмотрел, еше более печально, чем даже я описал.

Хм... нормальное поведение компилятора
Вызов родного метода, и естественно преобразование к типу.

А что не так? (я ни в с++, ни в с# - ни в зуб..., а тем более не теоретик)
Ну, статью прочту после этого поста Улыбаюсь
Записан

Мы все учились понемногу... Чему-нибудь и как-нибудь.
Dale
Блюзмен
Команда клуба

ru
Offline Offline
Пол: Мужской

WWW
« Ответ #14 : 01-12-2015 09:50 » 

А что не так? (я ни в с++, ни в с# - ни в зуб..., а тем более не теоретик)

Если совсем кратко, сюжет таков. В классе перегружены два метода с именем Foo: есть метод с сигнатурой void Foo(object o), а также переопределен унаследованный от предка виртуальный метод void Foo(int i). Автор статьи на Хабре считает более естественным, что для вызова d.Foo(i) будет использован второй метод, поскольку он лучше подходит по сигнатуре (без неявного преобразования типа аргумента).

На самом деле компилятор выбирает первый метод, неявно преобразуя int к object, и тем самым рвет шаблон многим читателям. Разработчик компилятора поясняет целесообразность такого выбора тем, что функциональность производного класса не изменится, даже если изменится базовый. Можно согласиться с таким выбором или нет, но принять его в итоге все равно придется.
Записан

Всего лишь неделя кодирования с последующей неделей отладки могут сэкономить целый час, потраченный на планирование программы. - Дж. Коплин.

Ходить по воде и разрабатывать программное обеспечение по спецификациям очень просто, когда и то, и другое заморожено. - Edward V. Berard

Любые проблемы в информатике решаются добавлением еще одного уровня косвенности – кроме, разумеется, проблемы переизбытка уровней косвенности. — Дэвид Уилер.
Страниц: [1]   Вверх
  Печать  
 

Powered by SMF 1.1.21 | SMF © 2015, Simple Machines