Forward declaration c что это
В теории программирования термин forward declaration означает декларирование идентификатора (обозначающего такой объект, как тип, переменная или функция) до того, как программист дал ему полное определение. Это требуется для того, чтобы компилятор знал тип идентификатора (чтобы можно было определить необходимый размер памяти для создания объекта, для проверки типа или подписи функции), при этом необязательно идентификатор может содержать значение (в случае переменной). Это также полезно для однопроходных компиляторов. Forward declaration используется в языках, которые требуют декларирования объекта перед его использованием (языки C, C++); это необходимо в таких языках для взаимной рекурсии (mutual recursion), поскольку невозможно определить эти функции без ссылки вперед в одном определении. Это также полезно для того, чтобы наглядно организовать структуру кода, например если Вы хотите разместить основную, главную часть кода в верхней части модуля, и ниже все вызываемые из этой главной части функции.
В других языках, где предварительное декларирование не нужно, обычно требуется вместо этого наличие многопроходного компилятора – идентификаторы должны быть определены (переменные инициализированы, тела функций определены) до того, как они будут использованы в процессе выполнения кода, но их не нужно определять до того, как они будут использованы в исходном коде для компиляции и интерпретации.
Давайте рассмотрим базовый пример предварительного декларирования функции на языке C:
В Pascal и других Виртовских языках программирования, главное правило гласит: все сущности должны быть декларированы перед использованием, так что нужно использовать forward declaration, что необходимо, например, для взаимной рекурсии (mutual recursion). На языке C это главное правило тоже работает, но здесь есть исключение для неопределенных функций и незавершенных типов. Таким образом, на C можно (хотя и нежелательно) реализовать пару взаимно рекурсивных функций:
В Pascal та же самая реализация требует forward declaration для второй функции, чтобы она могла использоваться в первой. Без наличия такой forward declaration компилятор выдаст ошибку, сообщающую о том, что второй идентификатор используется до того, как он был определен.
[Классы]
На языке C++ классы и структуры могут быть forward-declared примерно так:
На языке C++ классы могут быть forward-declared, если только Вам нужно использовать тип указателя на этот класс (поскольку все указатели на объекты имеют одинаковый размер, и об этом должен позаботиться компилятор). Особенно это полезно внутри определений класса, например если класс содержит в себе член, который является указываетeлем на другой класс; чтобы избежать цикличных ссылок (например, этот класс может также содержать член, который указывает на этот же класс), мы просто вместо этого делаем предварительное декларирование классов.
Forward declaration для класса недостаточно, если Вам нужно использовать действительный тип класса. Например, если Вы имеете член класса, который имеет тип не указателя, а напрямую задает класс, или если Вы используете его как базовый класс, или если Вам нужно использовать методы этого класса в другом методе.
На языке Objective-C классы и протоколы могут быть forward-declared примерно так:
Применять forward declaration для класса или протокола недостаточно, если Вам нужен подкласс subclass этого класса, или если надо реализовать этот протокол.
[Ссылка вперед (forward reference)]
Термин forward reference иногда используется как синоним forward declaration. Однако чаще это относится к реальному использованию объекта перед его определением; то есть первая ссылка над второй в коде перед объектом является forward reference. Так что можно сказать, что поскольку в Pascal обязательно нужно применить forward declaration, то forward reference в Pascal запрещены.
Пример (допустимой) forward reference на языке C++:
В этом примере есть две ссылки на myValue до того, как эта переменная была определена. C++ главным образом запрещает применять forward reference, но это разрешено в специальном случае для членов класса. Поскольку функция член класса accessor не может быть скомпилирована, пока компилятор не узнает тип члена класса переменной myValue, то это обязанность компилятора помнить определение accessor, пока он не увидит декларацию myValue.
Разрешение использования forward reference может значительно усложнить компиляцию и увеличить требования к памяти со стороны компилятора, и это обычно не дает реализовать компилирование за один проход.
Зачем нужен forward declaration в C++?
Не холивара ради, сравнение кода Swift и C++:
Я не понимаю почему в C++ нужно это в 2017 году
потому что не знаешь ничего о C++, иди читай Страуструпа или смирись и прогай для веба
А в свифте твоём связывание в рантайме происходит или в компайл, как в плюсах?
Ты так спрашиваешь, будто компилятор не может заглянуть вперед. Он, более того, в плюсах это все равно делает.
Я не понимаю почему в C++ нужно это в 2017 году, кто-нибудь может объяснить?
в С++ однопроходный компилятор.
Не аргумент (да и не факт что).
Ты так отвечаешь, как будто не знаешь про ODR и взаимную рекурсию.
Потому, что C++ древний язык и ломать обратную совместимость они не собираются.
А она бы сломалась?
А если у тебя только хидер и бинарная либа? Представляешь, на сколько увеличится время компиляции, если символы искать везде.
Скорее всего никто не предлагал, особенно забавляют ответы на stackoverflow
Imagine putting having two boxes and trying to put both into each other. Doesn’t work, right?
Представляешь, на сколько увеличится время компиляции, если символы искать везде
Some order dependencies are unavoidable in a language designed for one-pass compilation.
Тогда ясно, можно закрывать.
Хотя нет, на Quora говорят что с c++11 там multi-pass компилятор.
Я не понимаю почему в C++ нужно это в 2017 году, кто-нибудь может объяснить?
Потому что C++ делают комитетские мудаки. C++17 тому порукой.
А за использование ключевого слова «new» в современных плюсах нужно убивать.
оно изначально было designed for one-pass compilation. понятно что сейчас многопроходные везде компиляторы и это не актуально уже.
но менять это никто не будет, обратная совместимость дороже.
Я вообще все в shared_ptr оборачиваю, привычка из ObjC/Swift.
проход при этом задний?
Даже в C есть ссылки вперед, в C++ их еще больше.
И вообще, язык проектируют под однопроходную компиляцию чтобы компилятор был простой и быстрый. Это прямо в точку про C++.
А что нужно использовать в современных плюсах? malloc() и вручную вызывать конструктор?
Посмотрел бы я, во что ты превратишься, если придется работать в их условиях и выдавать их результат. Хотя, если тебе 15, то всё норм.
сходи в at&t расскажи, что они все сделали неправильно.
стд мейк юник видимо
сходи в at&t расскажи, что они все сделали неправильно.
Они догадываются. Но дело не в этом, а в том что аргументы в этом треде приводятся несостоятельные.
А что нужно использовать в современных плюсах?
Зависит от того что нужно. Есть emplace, есть умные указатели (3 штуки).
Страсть к разлапистым иерархиям и выпендрёжу с указателями очень быстро проходит: после первого же патологоанатомического сеанса с безвременно откинувшимся проектом.
Есть мнение что reference counting удел недалеких 😉
Я вообще все в shared_ptr оборачиваю
Угу. Как будто это гарантия. ШаредПтр весел и задорен, особенно если забыть что это тупая RAII-обёртка без единой капли «волшебного порошка» и какой-либо поддержки в языковом рантайме.
Знаешь я не часто вижу что программы на iOS или OSX падали, или страдали утечками, там везде RC и shared_ptr с weak_ptr. Никто не говорит что это гарантия.
Потому что C++ пытается здесь косить под C, который расчитан на однопроходный компилятор. По факту это уже давно не так, но никто не может это починить. Хрень с хидерами оттуда же растёт.
ломать обратную совместимость они не собираются.
Бвахахахахах! Охохохохох! Уахахахахахахах!
То-то в C++11/14/17 фишки стали выпиливать.
Есть мнение что reference counting удел недалеких 😉
Есть мнение, что все кто хотел свалить с C++ — давно уже свалили.
Я обычно такие высеры не читаю, но специально ради тебя прошёл по ссылке. Дочитал только до
Each time a smart pointer is passed along, there’s implicit overhead.
Извини, но аффтар не понимает как работает C++. А своё непонимание пытается скрыть за «седыми яйцами» и апломбом.
Выпиливают в основном всякую бессмыслицу.
Извини, но аффтар не понимает как работает C++. А своё непонимание пытается скрыть за «седыми яйцами» и апломбом.
На самом деле это ты чукча писатель, а не читатель. А аффтар на этот счёт пишет всё правильно.
Each time a smart pointer is passed along, there’s implicit overhead.
Извини, но аффтар не понимает как работает C++. А своё непонимание пытается скрыть за «седыми яйцами» и апломбом.
Ну да, всего лишь при каждом разыменовании кэш процессора слетает в задницу. Подумаешь какая мелочь.
Ну да, всего лишь при каждом разыменовании кэш процессора слетает в задницу.
Согласись, когда тебе действительно важно моральное состояние кешей процессора, произвольными указателями не пользуются, тем более умными.
Тебе какой-нибудь std::string натворит зла намного больше, да и любой (окромя std::array) стандартный контейнер в тёплых чувствах к кешу уличён не был.
разработчики компиляторов C++ и так мучаются с каждым новым стандартом
Согласись, когда тебе действительно важно моральное состояние кешей процессора, произвольными указателями не пользуются, тем более умными.
Учитывая, что указатели являются основным средством построения структур данных, выбора тут нет.
Тебе какой-нибудь std::string натворит зла намного больше, да и любой (окромя std::array) стандартный контейнер в тёплых чувствах к кешу уличён не был.
Я намекал на атомарность, которая необходима для подсчёта ссылок. Стандартные контейнеры тут не в тему.
Я намекал на атомарность, которая необходима для подсчёта ссылок.
Ну начинается. Мы, значит, затребовали от стандартной библиотеки сущность: а) безопасную в случае возникновения исключения; б) с атомарным счётчиком ссылок; и удивляемся чего это столько странного кода у нас появилось. Действительно.
При *разыменовывании* shared_ptr никаких накладных расходов нет: счётчик ссылок не изменяется. Более того, компилятор в некоторых случаях понимает, что в managed-часть можно и не лазить (восславим же, братья, великое UB!).
А во всех остальных случаях накладные расходы можно оценивать только в приложении к конкретной задаче, потому что оптимизация.
А в случае unique_ptr компилятор очень неплохо умеет делать оптимальный код.
Ну начинается. Мы, значит, затребовали от стандартной библиотеки сущность: а) безопасную в случае возникновения исключения; б) с атомарным счётчиком ссылок; и удивляемся чего это столько странного кода у нас появилось. Действительно.
Ты гонишь, я ничего ни у кого не требовал. Я лишь сказал, что чувак по ссылке прав, и что RC действительно несёт нетривиальные накладные расходы.
How do I forward declare an inner class? [duplicate]
I have a class like so.
Elsewhere, I want to pass a Container::Iterator by reference, but I don’t want to include the header file. If I try to forward declare the class, I get compile errors.
Compiling the above code gives.
How can I forward declare this class so I don’t have to include the header file that declares the Iterator class?
3 Answers 3
This is simply not possible. You cannot forward declare a nested structure outside the container. You can only forward declare it within the container.
You’ll need to do one of the following
I don’t believe forward declaring inner class of on an incomplete class works (because without the class definition, there is no way of knowing if there actually is an inner class). So you’ll have to include the definition of Container, with a forward declared inner class:
Then in a separate header, implement Container::Iterator:
Then #include only the container header (or not worry about forward declaring and just include both)
I know of no way to do exactly what you want, but here is a workaround, if you are willing to use templates:
Hopefully, this idiom might be of some use to you (and hopefully your compiler is EDG-based and implements export 😉 ).
Not the answer you’re looking for? Browse other questions tagged c++ forward-declaration or ask your own question.
Linked
Related
Hot Network Questions
site design / logo © 2021 Stack Exchange Inc; user contributions licensed under cc by-sa. rev 2021.12.7.40929
By clicking “Accept all cookies”, you agree Stack Exchange can store cookies on your device and disclose information in accordance with our Cookie Policy.
In C++, is it possible to forward declare a class as inheriting from another class?
I know that I can do:
but can I forward declare a class as inheriting from another, like:
An example use case would be co-variant reference return types.
. and then in another header that doesn’t include somewhere.h
5 Answers 5
A forward declaration is only really useful for telling the compiler that a class with that name does exist and will be declared and defined elsewhere. You can’t use it in any case where the compiler needs contextual information about the class, nor is it of any use to the compiler to tell it only a little bit about the class. (Generally, you can only use the forward declaration when referring to that class without other context, e.g. as a parameter or return value.)
Derived classes definitely need to know the structure of their parent, not just that the parent exists, so a forward declaration would be insufficient.
No it is not possible to forward declare inheritance, even if you are only dealing with pointers. When dealing with conversions between pointers, sometimes the compiler has to know the details of the class to do the conversion correctly. This is the case with multiple inheritance. (You could special case some parts parts of the hierarchy that only use single inheritance, but that isn’t part of the language.)
Consider the following trivial case:
The output I received (using 32 bit visual studio 2010), is:
So for multiple inheritance, when converting between related pointers, the compiler has to insert some pointer arithmetic to get the conversions right.
This is why, even if you are dealing only with pointers, you can’t forward declare inheritance.
As for why it would be useful, it would improve compile times when you do want to make use of co-variant return types instead of using casts. For example this will not compile:
This is useful when you have objects of type B (not pointers or references). In this case the compiler is smart enough to use a direct function call, and you can use the return type of RB* directly without casting. In this case, usually I go ahead and make the return type RA * and do a static cast on the return value.
All you needed to do was declare RB without the : public RA (oh, and also add ; to the end of your class definitions):
However, this doesn’t solve the specific problem described nicely in the answer by user1332054.
Some of the other answers appear to show some misconceptions that I’d like to dispel:
Forward declaring is useful even when when we know that the definition is not likely to be included. This allows us to do a lot of type-deduction in our libraries that make them compatible with many other established libraries without including them. Including libraries unnecessarily leads to too many nested includes that can explode the compile time. It’s good practice to make your code compatible when appropriate, and to include as little as possible.
Typically you can define a class with pointers to classes that have only been declared and not defined. Example:
The above compiles fine, because the compiler doesn’t need to know anything about B.
An example where it might be a bit harder to realise that the compiler needs more information:
The above problem is because new B invokes the B::B() constructor which hasn’t been defined yet. Also:
So, in reference to the more general problem, described by user1332054, I honestly don’t understand why it’s not possible to use pointers to undefined classed in an inherited virtual function.
More broadly though, I think that you’re making it more difficult for yourself by defining your classes instead of only declaring them. Here’s an example where you get to DoCleverStuff with your classes in your library before you’ve defined any of your classes at all:
What are forward declarations in C++?
The following is mentioned:
Can you explain «forward declaration» further? What is the problem if we use it in the main() function?
8 Answers 8
Why forward-declare is necessary in C++
The compiler wants to ensure you haven’t made spelling mistakes or passed the wrong number of arguments to the function. So, it insists that it first sees a declaration of ‘add’ (or any other types, classes, or functions) before it is used.
So, just to keep things explicit and avoid guessing, etc, the compiler insists you declare everything before it is used.
Difference between declaration and definition
As an aside, it’s important to know the difference between a declaration and a definition. A declaration just gives enough code to show what something looks like, so for a function, this is the return type, calling convention, method name, arguments, and their types. However, the code for the method isn’t required. For a definition, you need the declaration and then also the code for the function too.
How forward-declarations can significantly reduce build times
Break cyclic references where two definitions both use each other
Additionally, forward-declarations can help you break cycles. This is where two functions both try to use each other. When this happens (and it is a perfectly valid thing to do), you may #include one header file, but that header file tries to #include the header file you’re currently writing. which then #includes the other header, which #includes the one you’re writing. You’re stuck in a chicken and egg situation with each header file trying to re #include the other. To solve this, you can forward-declare the parts you need in one of the files and leave the #include out of that file.
File Car.h
File Wheel.h










