Я пытаюсь переместить определение функции шаблона из файла заголовка.
Я знаю, что для этого я могу явно создать экземпляр этой функции со всеми типами, которые я хочу видеть извне (во время компоновки). Однако внутри исходного файла (вместе с определением функции) у меня есть использование (неявное создание) функции со всеми необходимыми типами. Этот код работает и отлично линкуется с -O0, но не линкуется с -O2.
Я смог минимизировать проблему:
// a.cpp
#include "b.h"
int main() {
bar<int>();
return 0;
}
// b.h
template <class T> void bar();
// b.cpp
#include "b.h"
template <class T> void bar() {}
void foo() { bar<int>(); }
Если я скомпилирую b.cpp с параметрами -O0 и -O2, я получу другой набор символов, доступных в объектном файле:
> clang++ -c b.cpp -o b.o && nm -A b.o
b.o:0000000000000000 W _Z3barIiEvv
b.o:0000000000000000 T _Z3foov
> clang++ -c b.cpp -O2 -o b.o && nm -A b.o
b.o:0000000000000000 T _Z3foov
То же самое происходит и с gcc.
Похоже, что компиляторы встраивают созданную функцию и удаляют ее, так как не видят других ее применений.
Можно ли предотвратить удаление неявно созданных экземпляров функций шаблона без их явного создания?
Заранее спасибо!
UPD: этот ответ на связанный вопрос (как указал @Jarod42) отвечает на мой вопрос а также: по стандарту это невозможно сделать без явного инстанцирования.
bar<int>()
, которую можно вызвать изmain()
). Такой код нужен в вашем проекте, если вы хотите, чтобы программа линковалась. Если код работает без оптимизации, это ошибка компилятора или компоновщика. - person Peter   schedule 31.05.2019bar
.foo
вызываетbar<int>
, и поскольку у нас есть определение, мы должны сгенерировать тело дляbar<int>
, что и делает компилятор. После этого он встраивает свое тело вfoo
и удаляет его. Я не совсем понимаю понятие «попытка вызова», поскольку компоновщик ошибается только в функцииmain
, а не в функцииfoo
. - person Valeriy Savchenko   schedule 31.05.2019bar.cpp
является локальным для этого файла. Его нельзя вызвать изmain()
. - person Peter   schedule 31.05.2019template void bar<int>();
) вbar.cpp
он компилируется/связывается/запускается с оптимизацией и без нее, даже если определение является локальным дляb.cpp
. - person Valeriy Savchenko   schedule 31.05.2019bar<int>
можно было вызывать изmain()
. Как ни странно, то, как ваша программа строится без оптимизации, на самом деле не требуется стандартом C++, и неудача с оптимизацией ближе к правильному. Кроме того,bar<int>
на самом деле неявно создается вbar.cpp
, а не явно. Явное создание экземпляра отличается от того, что происходит из-за вызоваbar<int>
вbar.cpp
. В любом случае, чтобы найти решение вашей проблемы, поищите внешние шаблоны. - person Peter   schedule 31.05.2019template void bar<int>();
). Как указал @Jarod42, есть аналогичный вопрос, и есть ответ, цитирующий стандарт, что обходного пути нет. - person Valeriy Savchenko   schedule 31.05.2019