Подтвердить что ты не робот

Чистые виртуальные функции могут не иметь встроенного определения. Зачем?

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

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

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

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

-end пример]

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

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

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

Примечания:

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

Ответ 1

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

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

Итак, при выборе синтаксиса Бьярн думал о теле функции как о части инициализатора декларатора, а =0 в качестве альтернативной формы инициализатора, которая указывала "нет тела" (или, по его словам, "нет" ).

Понятно, что нельзя сказать "нет" и иметь тело. в этой концептуальной картине.

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

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

Ответ 2

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

Ответ 3

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

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

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

Ответ 4

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

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

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

Базовый линкер тогда должен знать о С++. Он может просто соединяться вместе, хотя вы объявили его как = 0.

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

Ответ 5

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

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

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

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

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

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

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