Компилятор оптимизирует переменную, которая никогда не используется, он не может оптимизировать функцию на том основании, что она не будет использоваться, поскольку она может использоваться из другого модуля компиляции. Вы можете попытаться заставить компилятор учитывать переменную, которая используется с чем-то похожим на:
void instantiation()
{
Foo<int> f;
f; // mark the variable as if it is used.
}
// or:
Foo<int>* instantiation()
{
Foo<int> *p = new Foo<int>();
return p; // The compiler cannot know if p will be used, it must compile
}
Лучшим решением было бы явное создание экземпляра шаблона, если вы хотите:
// .h
template <typename T>
class Foo
{
public:
Foo( T const & value );
void set( T const & ); // whatever else
private:
T value_;
};
// template implementation another file, not included from .h
// instantiation.cpp??
template <typename T>
Foo<T>::Foo<T>( T const & value ) : value_(value) {}
template <typename T>
void Foo<T>::set( T const & v )
{
value_ = value;
}
// explicit instantiation
template class Foo<int>;
template class Foo<double>;
// test.cpp
#include "header.h"
int main()
{
Foo<int> f(5);
f.set( 7 );
Foo<char> f2; // linker error Foo<char>() not defined
}
Код пользователя будет видеть только заголовок и знать, какие методы существуют, но не реальную реализацию. Реализация будет скомпилирована в одном модуле компиляции, где происходит явная реализация шаблона.
Обратите внимание, что если вы забудете явно создать экземпляр одного типа, это будет ошибка компоновщика, а не ошибка компиляции.
Правило единого определения
Одно правило определения в c ++ гласит, что может быть только одно определение для каждого символа или класса. Наличие нескольких определений может быть легко обнаружено для обычных символов (если вы определите два void f() { }
, компоновщик обнаружит дублированный символ), но это немного сложнее с шаблонами. С шаблонами это сложнее, так как они обычно объявляются и определяются в заголовочных файлах. Компилятор генерирует используемые символы в каждой единице компиляции [1], и компоновщик обычно находит более одного эквивалентного символа (std :: vector :: push_back () компилируется в каждую единицу компиляции, которая имеет std :: vector и вызывает push_back)
Компилятор помечает шаблонный код как «слабый» символ, означающий, что, хотя этот символ определен здесь, он также может быть определен в другом модуле компиляции, и компоновщик может отказаться от символа, не приводя к ошибке ссылки. Это необходимо, если вы хотите связать разные модули компиляции, которые используют одни и те же средства STL, например, с одинаковыми типами.
До gcc 4.2, gcc linux linker отбрасывает все, кроме одного из слабых символов, без дальнейшей проверки. Некоторые линкеры (gcc linker в linux будет в ближайшем будущем, а не как 4.2, не знают ни 4.3, ни 4.4, это может быть там), проверяют, что разные «слабые» символы на самом деле одинаковы и выдают ошибку / предупреждение пользователю.
Ваш код нарушает ODR в том смысле, что вы переопределяете шаблон в другом месте. Вы должны объявить шаблон один раз и реализовать методы извне, как указано выше. В любом случае, если оба определения совместимы (как и в опубликованном вами фрагменте): все методы и атрибуты-члены в точности совпадают и имеют одинаковые квалификаторы (virtual / const-ness ...), компилятор должен принять их, так как только одно определение (увы, повторяется) шаблона.
[1] Будут скомпилированы только те методы, которые фактически вызываются в коде:
template <typename T>
struct Test
{
void f() { std::cout << "f()" << std::endl; }
void g() { std::cout << "g()" << std::endl; }
};
int main()
{
Test<int> t;
t.f(); // compiler generates Test<int>::f, but not Test<int>::g
}