Я привык писать код без модулей, где заголовочные файлы содержат объявления функций, такие как:
// foo.h
class Foo
{
void bar();
};
и соответствующий файл .cpp содержит определение:
// foo.cpp
#include "foo.h"
void Foo::bar()
{
// ...
}
Насколько мне известно, это сделано для уменьшить время компиляции и уменьшить зависимости. когда модули будет использоваться, это все еще будет применяться? Было бы так же быстро разместить класс в одном файле с определениями, как это делают Java и C #? Если это так, будет ли необходимость в обоих .hpp
а также .cpp
файлы при использовании модулей?
Есть еще много причин использовать файлы заголовков.
Простота совместного использования и понимания объектного API, не видя базовых деталей, достаточно полезна, чтобы держать их рядом. Это хороший 20-футовый обзор объекта, по сути являющийся контуром.
Если вы продаете библиотеку, вы должны включить заголовочный файл, а также файл архива или общую библиотеку. Таким образом, вы можете сохранить конфиденциальность информации без ущерба для IP вашего продукта, и ваши клиенты могут включить двоичный файл, скомпилированный для своей цели.
Я не верю, что это возможно без заголовочных файлов.
Единственная причина, по которой я знаю, как предложение модулей в настоящее время стоит, должен обрабатывать зависимости кругового интерфейса.
Если программа состоит из модулей и не отделяет объявления функций от определений, все файлы модулей будут интерфейсами модулей (в отличие от реализаций модулей). Если вы хотите сравнить их с заголовочными файлами и файлами кода, интерфейсы модуля можно рассматривать как файл заголовка (.hpp), а реализации модуля можно рассматривать как файлы кода (.cpp).
К сожалению, предложение модулей не допускает циклических зависимостей интерфейса модуля. А поскольку ваша программа теперь полностью состоит из интерфейсов модулей, вы никогда не сможете иметь два модуля, которые каким-либо образом зависят друг от друга (это может быть улучшено proclaimed ownership
объявление в будущем, но это в настоящее время не поддерживается). Единственный способ разрешить циклические зависимости интерфейса модуля — разделить объявления и определения и поместить циклический импорт в файлы реализации модуля, в отличие от циклических зависимостей интерфейса модуля, допускаются циклические зависимости реализации модуля.
Следующий код предоставляет пример ситуации, которую невозможно скомпилировать без разделения объявлений и определений:
Foo module file
export module Foo;
import module Bar;
export namespace Test {
class Foo {
public:
Bar createBar() {
return Bar();
}
};
}
Bar module file
export module Bar;
import module Foo;
export namespace Test {
class Bar {
public:
Foo createFoo() {
return Foo();
}
};
}
Эта статья показывает пример того, как это можно решить, были proclaimed ownership
объявление доступно. По сути, все сводится к разделению деклараций и определений.
В идеальном мире компилятор мог бы справиться с этим сценарием, но, увы, насколько мне известно, текущая предлагаемая реализация модулей не поддерживает его.
Есть хорошая дискуссия Вот это объясняет идею модулей.
Короче говоря, вы правы, разделение между заголовочными файлами и файлами реализации больше не потребуется. #include
директива будет заменена import
директива, и во время компиляции модуль предоставит необходимую информацию, которая в противном случае была бы во включенном заголовочном файле.
Другое использование этой идиомы унаследовано от C; это удобный способ предварительного объявления даже для единиц перевода, которые не зависят от других единиц перевода.
Использование предварительно скомпилированных заголовков действительно не стало важной вещью, пока расширенное использование заголовочных файлов в C ++ не сделало это необходимым по соображениям производительности (несмотря на некоторые очень большие заголовки старой школы, такие как windows.h).
Идея состоит в том, чтобы ввести в игру нечто большее, чем механизм C # / Java. Механизм C ++ очень по духу Modula / ADA. Было бы неплохо, чтобы машины делали больше за нас.