Поскольку ответ Брайана дает вам рабочее решение и объясняет, почему вам не нужен extern, я подробнее остановлюсь на вашей ситуации.
Во-первых, вы заявили, что ваш компилятор падает на вас, поэтому вы предполагали, что это ошибка компилятора. Это не фактический случай. С вашим кодом, как это было, A.cpp
, B.cpp
& main.cpp
все успешно скомпилировано самостоятельно. Не было ошибок компиляции. В Visual Studio 2017 CE не было, пока я не попытался собрать программу, пока она не рухнула. Вот ошибка сборки моей IDE.
1>------ Build started: Project: Temp, Configuration: Debug Win32 ------
1>B.obj : error LNK2005: "void __cdecl foo<int>(void)" (??$foo@H@@YAXXZ) already defined in A.obj
1>main.obj : error LNK2005: "void __cdecl foo<int>(void)" (??$foo@H@@YAXXZ) already defined in A.obj
1>C:\Users\...\Temp.exe : fatal error LNK1169: one or more multiply defined symbols found
1>Done building project "Temp.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Здесь у вас ошибка Linking
. Невозможно установить связь из-за нескольких определенных символов.
Давайте рассмотрим ваш код:
хиджры
#pragma once
#include <iostream>
// Not just a declaration of void foo<T>() but also a definition!
template<typename T> void foo() { std::cout << "default stuff\n"; }
// Not just a declaration of void foo<int>() but also a specialized definition!
template<> void foo<int>() { std::cout << "int stuff\n"; }
// external explicit instantiation
extern template void foo<int>();
a.cpp
#include "A.h"
// explicit instantiation
template void foo<int>();
B.h
#pragma once
void bar();
B.cpp
#include "B.h"
#include "A.h"
void bar() {
// instantiation to call foo
foo<int>();
}
main.cpp
#include "A.h"
#include "B.h"
int main() {
// instantiation to call foo
foo<int>();
bar();
return 0;
}
То, что здесь происходит, - все 3 компилируются, но когда он идет к компоновщику, чтобы создать один исполняемый файл, передавая три объектных файла, он терпит неудачу. Компилятор просто проверяет синтаксис грамматики языка и преобразует его в объектные файлы. Компоновщик получает объектные файлы от компилятора и создает все необходимые символы для переменных, функций, классов и т. Д.
Он выглядит в основном и видит #include "A.h"
и #include "B.h"
Итак, прекомпилятор уже выполнил подстановку текста и вставил A.h
и B.h
вверху страницы, так что весь код был в A.h
& B.h
, относящиеся к A.cpp
и B.cpp
единицам перевода, теперь также находятся в main.cpp
единице перевода. Таким образом, он видит объекты шаблона foo
и видит более одного определения! Это не имеет никакого отношения к использованию вами extern. Чтобы продемонстрировать это, я могу удалить часть вашего кода и по-прежнему генерировать ту же ошибку сборки, когда не удается связать ее из-за нескольких определений.
хиджры
#pragma once
#include <iostream>
template<typename T> void foo() { std::cout << "default stuff\n"; }
template<> void foo<int>() { std::cout << "int stuff\n"; }
a.cpp
#include "A.h"
main.cpp
#include "A.h"
int main() {
foo<int>();
return 0
}
Дайте в основном ту же ошибку компоновки сборки:
1>------ Build started: Project: Temp, Configuration: Debug Win32 ------
1>main.cpp
1>main.obj : error LNK2005: "void __cdecl foo<int>(void)" (??$foo@H@@YAXXZ) already defined in A.obj
1>C:\Users\...\Temp.exe : fatal error LNK1169: one or more multiply defined symbols found
1>Done building project "Temp.vcxproj" -- FAILED.
========== Build: 0 succeeded, 1 failed, 0 up-to-date, 0 skipped ==========
Единственное различие между ними состоит в том, что множественное определение находится в A
& B
, где во 2-м экземпляре, даже не используя B
, найдено множественное определение A
.
Чтобы решить эту проблему, используйте Brian's
ответ! Основное правило заключается в том, чтобы иметь declarations
в headers
и definitions
в cpp
файлах, если только ваши определения не находятся в определенном классе или пространстве имен, а не в глобальной области видимости.