Чистые виртуальные функции могут не иметь встроенного определения.Зачем? - PullRequest
58 голосов
/ 14 ноября 2010

Чистые виртуальные функции - это те функции-члены, которые являются виртуальными и имеют спецификатор pure (= 0;)

Пункт 10.4 параграфа 2 C ++ 03 говорит нам, что такое абстрактный класс, и, в качестве дополнительного примечания, следующее:

[Примечание: объявление функции не может предоставить ни чистый спецификатор, ни определение - Конечная заметка] [Пример:

struct C {
virtual void f() = 0 { }; // ill-formed
};

- конец примера]

Для тех, кто не очень знаком с проблемой, обратите внимание, что чисто виртуальные функции могут иметь определения , но вышеупомянутое предложение запрещает такие определения показывать встроенными (лексически в -учебный класс). (При использовании определения чисто виртуальных функций вы можете увидеть, например, this GotW )

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

Мой вопрос: знает ли кто-нибудь эти конкретные причины? Хорошо также приветствуются догадки.

Примечания:

  • MSVC позволяет PVF иметь встроенные определения. Так что не удивляйтесь:)
  • слово inline в этом вопросе не относится к ключевому слову inline . Это должно означать лексически в классе

Ответы [ 5 ]

52 голосов
/ 18 ноября 2010

В потоке SO & ldquo; Почему чисто виртуальная функция инициализируется 0? & Rdquo; Джерри Коффин предоставил эту цитату из Bjarne Stroustrup & srs Дизайн и эволюция C ++ , раздел §13.2.3, где я добавил некоторые акценты той части, которая, на мой взгляд, актуальна:

Любопытный синтаксис =0 был выбран более очевидная альтернатива введения новое ключевое слово, чистое или абстрактное, потому что в то время я не видел шансов получить новое ключевое слово принято. Если бы я предложил чистый, релиз 2.0 будет иметь поставляется без абстрактных классов. Учитывая выбор между более приятным синтаксисом и абстрактные классы, я выбрал абстрактные классы. Вместо того, чтобы рисковать задержкой и навлекать определенные бои за чисто я использовал традицию C и C ++ соглашение об использовании 0 для представления "не там." Синтаксис =0 соответствует мое мнение, что тело функции является инициализатор для функции , а также с (упрощенно, но обычно адекватно) просмотр набора виртуальных функций реализуется как вектор функциональные указатели. [& hellip; ]

Таким образом, при выборе синтаксиса Бьярн думал о теле функции как о некой части инициализатора объявления, а =0 как об альтернативной форме инициализатора, которая указала на & ldquo; no body & rdquo; (или, по его словам, «там нет»).

Само собой разумеется, что нельзя одновременно указать & ldquo; not there & rdquo; и иметь тело & ndash; в этой концептуальной картине.

Или, все еще в этой концептуальной картине, с двумя инициализаторами.

Это мои телепатические способности, google-foo и мягкие рассуждения. Я предполагаю, что никто не был достаточно заинтересован & trade; сформулировать предложение для комитета об отмене этого чисто синтаксического ограничения и последующей работе со всеми вытекающими последствиями. Таким образом, это все еще так.

8 голосов
/ 14 ноября 2010

Вы не должны так сильно верить в комитет по стандартизации. Не у всех есть глубокие причины, чтобы это объяснить. Что-то так просто, потому что сначала никто не думал иначе, а после никто не думал, что его изменение достаточно важно (я думаю, что это так); для достаточно старых вещей это может быть даже артефакт первой реализации. Некоторые из них являются результатом эволюции - в то время существовала глубокая причина, но причина была устранена, и первоначальное решение больше не пересматривалось (это также может иметь место в случае, когда первоначальное решение было потому, что любое определение чистая функция была запрещена). Некоторые из них являются результатом переговоров между различными POV, и результату не хватает согласованности, но этот недостаток был сочтен необходимым для достижения консенсуса.

7 голосов
/ 24 ноября 2010

Хорошие догадки ... хорошо, учитывая ситуацию:

  • допустимо объявлять функцию встроенной и предоставлять явно встроенное тело (вне класса), поэтому нет никаких возражений противтолько практический смысл быть объявленным внутри класса.
  • Я не вижу потенциальных двусмысленностей или противоречий в грамматике, поэтому нет логической причины для исключения определений функций in situ .

Мое предположение: использование тел для чисто виртуальных функций было реализовано после того, как была сформулирована грамматика = 0 | { ... }, а грамматика просто не была пересмотрена.Стоит учесть, что существует множество предложений об изменениях / улучшениях языка, в том числе о том, чтобы сделать подобные вещи более логичными и последовательными, но число, которое кто-то взял и записал в качестве официальных предложений, намного меньше, а числоиз них у Комитета есть время рассмотреть, и, по его мнению, поставщики компиляторов будут готовы к внедрению, он снова намного меньше.Подобным вещам нужен чемпион, и, возможно, вы первый, кто видит в этом проблему.Чтобы прочувствовать этот процесс, посмотрите http://www2.research.att.com/~bs/evol-issues.html.

2 голосов
/ 22 ноября 2010

Хорошие догадки приветствуются?

Я думаю, что = 0 в объявлении происходит из соображений реализации. Скорее всего, это определение означает, что вы получаете запись NULL в RTTI vtbl информации о классе - место, где во время выполнения хранятся адреса функций-членов класса.

Но на самом деле, когда вы помещаете определение функции в файл *.cpp, вы вводите имя в объектный файл для компоновщика: адрес в *.o файл, где найти конкретную функцию.

Тогда базовому компоновщику нужно больше знать о C ++. Он может просто соединяться, даже если вы объявили его как = 0.

Мне кажется, я читал, что возможно то, что вы описали, хотя я забыл поведение: -) ...

0 голосов
/ 03 января 2013

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

Единственный способ фактически получить вызываемую реализацию - этоиспользование синтаксиса Base :: func () от одной из перегрузок производного класса.

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

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

(Кстати, я предполагаю, что Base::f() можно вызывать только с этим синтаксисом из Derived::f() ине из Derived::anyOtherFunc(). Я прав с этим предположением?).

Чистые виртуальные деструкторы - это отдельная история, в некотором смысле.Он используется в качестве техники просто для предотвращения создания кем-либо экземпляра производного класса без каких-либо чистых виртуальных функций где-либо еще.

Ответ на реальный вопрос «почему» это не разрешено, на самом деле только потому, чтотак сказал комитет по стандартам, но мой ответ проливает некоторый свет на то, чего мы пытаемся достичь в любом случае.

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