Как использовать явное создание экземпляра шаблона с модулями C++20?
Как объясняется в этом ответе, создание экземпляра шаблона позволяет сократить время и размеры компиляции, не требуя перекомпиляции шаблонов для каждого нового типа в каждом новом файле, который их использует.
Я также взволнован тем, как модули C++20 должны предоставлять чистое решение для предоставления шаблонов внешним проектам и уменьшения дублирования hpp/cpp.
Какой синтаксис позволит им работать вместе?
Например, я ожидаю, что модули будут немного похожи (непроверенный и, следовательно, скорее всего неправильный код, потому что у меня нет достаточно нового компилятора / я не уверен, что он еще реализован):
helloworld.cpp
export module helloworld;
import <iostream>;
template<class T>
export void hello(T t) {
std::cout << t << std::end;
}
helloworld_impl.cpp
export module helloworld_impl;
import helloworld;
// Explicit instantiation
template class hello<int>;
main.cpp
// How to prevent the full definition from being imported here, which would lead
// hello(1) to instantiate a new `hello<int>` instead of reusing the explicit instantiated
// one from `helloworld_impl.cpp`?
import helloworld;
int main() {
hello(1);
}
а затем компиляция, упомянутая на https://quuxplusone.github.io/blog/2019/11/07/modular-hello-world, будет вместе (?)
clang++ -std=c++2a -c helloworld.cpp -Xclang -emit-module-interface -o helloworld.pcm
clang++ -std=c++2a -c -fprebuilt-module-path=. -o helloworld_impl.o helloworld_impl.cpp
clang++ -std=c++2a -fprebuilt-module-path=. -o main.out main.cpp helloworld_impl.o
В идеале я также хочу, чтобы определение шаблона можно было использовать во внешних проектах.
Я думаю, что мне нужен способ импортировать модуль, и во время импорта выбрать один из следующих вариантов:
- использовать все шаблоны в модуле, как если бы они были только объявлениями (я предоставлю свои собственные экземпляры в другом файле)
- используйте шаблоны в модуле, как если бы они были определениями
Это в основном то, что я достиг в pre-C++20 в разделе "Удалите определения из включенных заголовков, но также предоставьте шаблоны внешнего API", но для этой настройки требуется дважды копировать интерфейсы, что похоже на то, что модульная система может по существу сделать за нас.
1 ответ
Модули упрощают задачу "быстрой одиночной сборки". Они мало что делают для "поддержки создания экземпляров клиента, но избегают перестроения клиентов явно созданных специализаций"; теория состоит в том, что обычно более быстрые сборки, избегающие повторяющейся работы, делают ненужным искажение программы, чтобы сэкономить еще больше времени.
Все, что вы делаете, это помещаете явное определение экземпляра в интерфейс модуля:
export module A;
export template<class T>
inline void f(T &t) {++t;}
template void f(int&);
template void f(int*&);
Импортерам не нужно создавать экземпляры f
для любого из этих двух типов, даже если шаблон функции является встроенным (что может потребовать дополнительных экземпляров в немодульном коде). Типичная реализация кэширует результаты этих экземпляров в скомпилированном файле интерфейса модуля с достаточной детализацией для встроенных вызовов в импортере (а также кэширует сам шаблон с достаточной детализацией для его дальнейшего создания).
Конечно, вы также можете использовать явное объявление создания экземпляра с простым объявлением шаблона в интерфейсе, а также определить шаблон и поместить явные определения экземпляра в модуль реализации модуля, но это не отличается от того, как работают файлы заголовков.