c ++ 20 — При использовании модулей C ++ есть ли какая-либо причина отделять объявления функций (файлы .hpp) от их определений (файлы .cpp)?

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

// foo.h
class Foo
{
void bar();
};

и соответствующий файл .cpp содержит определение:

// foo.cpp
#include "foo.h"
void Foo::bar()
{
// ...
}

Насколько мне известно, это сделано для уменьшить время компиляции и уменьшить зависимости. когда модули будет использоваться, это все еще будет применяться? Было бы так же быстро разместить класс в одном файле с определениями, как это делают Java и C #? Если это так, будет ли необходимость в обоих .hpp а также .cpp файлы при использовании модулей?

13

Решение

Есть еще много причин использовать файлы заголовков.

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

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

Я не верю, что это возможно без заголовочных файлов.

2

Другие решения

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

Если программа состоит из модулей и не отделяет объявления функций от определений, все файлы модулей будут интерфейсами модулей (в отличие от реализаций модулей). Если вы хотите сравнить их с заголовочными файлами и файлами кода, интерфейсы модуля можно рассматривать как файл заголовка (.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 объявление доступно. По сути, все сводится к разделению деклараций и определений.

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

2

Есть хорошая дискуссия Вот это объясняет идею модулей.

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

1

Другое использование этой идиомы унаследовано от C; это удобный способ предварительного объявления даже для единиц перевода, которые не зависят от других единиц перевода.

Использование предварительно скомпилированных заголовков действительно не стало важной вещью, пока расширенное использование заголовочных файлов в C ++ не сделало это необходимым по соображениям производительности (несмотря на некоторые очень большие заголовки старой школы, такие как windows.h).

Идея состоит в том, чтобы ввести в игру нечто большее, чем механизм C # / Java. Механизм C ++ очень по духу Modula / ADA. Было бы неплохо, чтобы машины делали больше за нас.

1