Инстанцируют ли явные экземпляры шаблонов классов C++ зависимые базовые классы?
Я предполагал, что явный запрос на создание экземпляров автоматически создаст экземпляр всех членов базового класса, но я получаю linker error: unresolved external symbol "public: void Base<int>::foo(int)"
при построении этого кода с использованием Visual Studio 2008 или 2010.
Обратите внимание, что добавление вызова foo()
внутри bar()
заставляет компилятор создавать экземпляры Base<int>::bar()
и сборка завершается успешно, поэтому кажется, что компилятор имеет всю необходимую информацию для создания экземпляра foo()
,
Очевидно, в явном виде Base<int>
В source.cpp сборка успешна, но кажется глупым необходимость явно создавать экземпляры любых зависимых базовых классов всякий раз, когда явно создается производный класс.
Это нормально? Я не мог найти то, что стандарт говорит по этому вопросу.
header.h
template<typename T>
class Base {
public:
void foo();
};
template<typename T>
class Derived : public Base<T> {
public:
void bar();
};
source.cpp
#include "header.h"
template<typename T>
void Base<T>::foo() { }
template<typename T>
void Derived<T>::bar() {
// this->foo(); // adding this forces instantiation of foo()???
}
template class Derived<int>;
main.cpp
#include "header.h"
int main() {
Derived<int> d;
d.foo(); // Linker Error: unresolved external symbol "public: void Base<int>::foo(int)"
}
Редактировать:
Похоже, что стандарт говорит, что только члены класса создаются с помощью явного создания экземпляра класса, поэтому ошибка компоновщика оправдана в моем примере.
Обратите внимание, что класс определяется с помощью class-head {спецификация-члена} и "Спецификация члена в определении класса объявляет полный набор членов класса; ни один член не может быть добавлен в другом месте". Таким образом, члены находятся только между фигурными скобками { }, и открытые члены базового класса не становятся членами производного класса, они просто доступны из производного класса или из объектов производного класса.
Мой единственный оставшийся вопрос - почему Стандарт определяет, что явное создание экземпляра шаблона класса только создает экземпляры членов, а не членов базовых классов? Я предполагаю, что это позволяет лучше контролировать то, что и где явно создается. Тот, кто использует явные экземпляры класса шаблона, скорее всего, будет иметь определения базового класса в файле, отличном от определений производного класса, и будет явно создавать каждое из них отдельно.
1 ответ
Стандарт говорит
Явное создание экземпляра специализации шаблона класса подразумевает создание всех его членов, которые ранее явно не специализировались на модуле перевода, содержащем явное создание экземпляра.
Другими словами, он не требует, чтобы базовые классы были явно созданы в свою очередь. Это вызовет неявное создание их экземпляров, что не приведет к предварительному созданию определений их членов. В Стандарте есть какая-то уродливая ошибка в отношении того, означает ли какой-либо текст, когда говорится "член", "прямым" или "унаследованным" членом, поскольку это часто кажется "очевидным" для того, кто написал формулировку Стандартов, но не для тот, кто читает это. C++0x добавил некоторые пояснения (он также имеет разницу между объявлениями явного создания экземпляров и определениями, которых нет в C++03, но даже игнорируя это, формулировка C++ 0x содержит еще немного понимания):
Явное создание экземпляра, который называет специализацию шаблона класса, также является явным созданием экземпляра того же типа (объявление или определение) каждого из его членов (не включая членов, унаследованных от базовых классов), которые ранее не были явно специализированы в модуле перевода, содержащем явная реализация, за исключением случаев, описанных ниже. [Примечание: Кроме того, обычно это будет явное создание определенных зависящих от реализации данных о классе. - конец примечания]