Использование extern template class
, похоже, не предотвращает встраивание. Я проиллюстрирую это на примере, он немного сложен, но самое простое, что я могу придумать.
В файле a.h мы определяем шаблон класса CFoo
,
#ifndef A_H
#define A_H
#include <iostream>
template <typename T> class CFoo{
public: CFoo(){
std::cout << "CFoo Constructor, edit 0" << std::endl;
}
};
extern template class CFoo<int>;
#endif
В конце a.h мы используем extern template class CFoo<int>
, чтобы указать любой единице перевода с #include a.h
, что ей не нужно генерировать какой-либо код для CFoo. Мы обещаем, что все вещи CFoo будут гладко связаны.
В файле c.cpp у нас есть
#include "a.h"
void run(){
CFoo<int> cf;
}
В связи с необходимостью extern template class
promise' at the end of a.h, the translation unit of c.cpp does not
генерировать любой код для класса CFoo.
Наконец, мы объявляем главную функцию в b.cpp,
void run();
int main(){
run();
return 0;
}
В b.cpp нет ничего фантастического, мы просто объявляем void run()
, что будет связано с реализацией модуля перевода b.cpp во время компоновки. Для полноты вот make-файл
cflags = -std=c++11 -O1
b : b.o a.o c.o
g++ ${cflags} b.o a.o c.o -o b
b.o : b.cpp
g++ ${cflags} -c b.cpp -o b.o
c.o : c.cpp
g++ ${cflags} -c c.cpp -o c.o
a.o : a.cpp a.h
g++ ${cflags} -c a.cpp -o a.o
clean:
rm -rf a.o b.o c.o b
Используя этот make-файл, компилируется и связывается исполняемый файл a , который выводит `` CFoo Constructor, edit 0 '' при запуске. Но обратите внимание! В приведенном выше примере мы, кажется, нигде не объявили CFoo<int>
: CFoo<int>
определенно не объявлено в модуле перевода b.cpp, так как заголовок не отображается в этом модуле перевода, и модулю перевода c.cpp сказали, что он не нужно было реализовывать CFoo. Так что же происходит?
Внесите одно изменение в make-файл: замените -O1 на -O0 и сделайте чистый make
Теперь вызов по ссылке приводит к ошибке (при использовании gcc 4.8.4)
c.o: In function `run()':
c.cpp:(.text+0x10): undefined reference to `CFoo<int>::CFoo()'
Это ошибка, которую мы могли бы ожидать, если бы не было встраивания в первую очередь. По крайней мере, к такому выводу я пришел, дальнейшие идеи очень приветствуются.
Чтобы получить связь с -O1, нам нужно выполнить наше обещание и предоставить реализацию CFoo, которую мы предоставляем в файле a.cpp
#include "a.h"
template void foo<int>();
Теперь мы можем быть уверены, что CFoo появится в модуле перевода a.cpp, и наше обещание будет выполнено. Кроме того, обратите внимание, что template void foo<int>()
в a.cpp предшествует extern template void foo<int>()
через включение a.h, что не является проблематичным.
Наконец, я нахожу это непредсказуемое поведение, зависящее от оптимизации, раздражающим, поскольку это означает, что изменения в ah и перекомпиляция a.cpp могут не отражаться в run()
, как ожидалось, если бы не было встраивания (попробуйте изменить стандартный вывод конструктора Foo и переделать).