Возможно прочитать ошибки компоновщика с небольшим усилием. Давайте попробуем:
main.obj : error LNK2019: unresolved external symbol "public: __thiscall list<int,100>::~list<int,100>(void)" (??1?$list@H$0GE@@@QAE@XZ) referenced in function __catch$_main$0
- Ошибка возникает в
main.obj
, то есть объектном файле, созданном из main.cpp.
- Проблема в «неразрешенном внешнем символе». То есть вы ссылаетесь на символ, который, как ожидается, будет определен в другом модуле компиляции, но компоновщик не может найти это определение.
- Данный символ является открытой функцией-членом (__thiscall - соглашение о вызовах для функций-членов).
- Теперь мы подошли к действительно полезной информации:
list<int,100>::~list<int,100>(void)"
говорит нам, что проблема в деструкторе списка шаблонов классов, специализированного для <int, 100>
. Эта часть красиво отформатирована и совершенно проста. Для второй ошибки мы также видим тип возвращаемого значения: int __thiscall list<int,100>::return_current(void)
. То есть функция, которая возвращает int, использует соглашение о вызовах __thiscall, принадлежит списку, называется return_current и не принимает параметров.
??1?$list@H$0GE@@@QAE@XZ
можно в значительной степени игнорировать. Это имя символа, искаженное компилятором, и поэтому это имя, которое компоновщик ищет в файле .obj. Но поскольку компоновщик уже сказал нам читаемое имя символа в C ++, нам это на самом деле не нужно. (если вы не решите сами копаться в файле .obj).
- Наконец, он говорит нам, что на символ ссылаются в функции main. (Общее правило при чтении ошибок компоновщика - игнорировать биты, которые вы не понимаете. И я не совсем уверен, что означает здесь часть «catch», но она связана с обработкой исключений, которую вы делаете внутри main.
Так что у вас это есть. Первая ошибка гласит, что определение деструктора списка не найдено. Если функция не объявлена встроенной в определении класса, она должна быть определена в другом файле .cpp. Я предполагаю, что это ваш list_def.cpp
, и этот файл также компилируется и передается компоновщику.
Однако это приводит нас к небольшой проблеме с шаблонами. Это конструкции времени компиляции, и шаблон должен быть создан до того, как компилятор выпустит для него код. В выводе вашего компилятора не существует кода ни для шаблона класса list
, ни для специализации list<int, 84>
, поскольку эта специализация не используется. Компилятор генерирует только те специализации, которые действительно необходимы.
И когда компилятор обрабатывает list_def.cpp, нет специализаций, кажется, требуются. Он не может видеть другие файлы .cpp, такие как ваш main.cpp. Он видит только файл .cpp, который в данный момент компилируется, и все, что это #include
s. Поскольку он не видит list<int, 100>
, он не генерирует никакого кода для этой специализации, а затем, когда объектные файлы передаются компоновщику, он не может найти определение символа list<int, 100>
и выдает ошибку .
Обычным решением является определение всех членов шаблонов классов, встроенных в заголовок. Таким образом, определение видимо для любого модуля компиляции, включая заголовок, и поэтому компилятор может создать необходимые специализации шаблонов.
В частности, следующее приведет к ошибке компоновщика:
// .h
template <int n>
class Foo {
int Bar();
};
// .cpp
template <int n>
int Foo::Bar() {
return n; // Error: This is not visible from other .cpp files
}
Так что просто переместим содержимое .cpp в заголовок:
// .h
template <int n>
class Foo {
int Bar();
};
template <int n>
int Foo::Bar() {
return n; // Error: This is will cause the function to be defined in every .cpp file that includes it, so you'll get a *different* linker error instead (multiple definitions)
}
Но это работает, и это обычное решение
// .h
template <int n>
class Foo {
int Bar() { return n; } // just define it here, inside the class definition, and it is implicitly inline, so it's ok that multiple .cpp files see it
};
Или альтернативно:
// .h
template <int n>
class Foo {
int Bar();
};
// still in .h
template <int n>
inline int Foo::Bar() { // explicitly marking it inline works too. Now the compiler knows that it might be defined in multiple .cpp files, and these definitions should be merged back together
return n;
}
Более необычным, но иногда полезным решением является явное создание экземпляра шаблона в модуле компиляции, который его определяет. Поэтому в list_def.cpp
добавьте эту строку после определения шаблона:
template class list<int, 100>;
Это указывает компилятору специально генерировать код для этой специализации, даже если он не используется иным образом в этом модуле компиляции. И, очевидно, этот подход полезен, только если вы заранее знаете, какие специализации понадобятся.
Edit:
Похоже, вы никогда не определяете функцию clear()
, которая вызывается из деструктора. Это является причиной последней ошибки компоновщика, которую вы получаете после включения всего в заголовок.