Явная реализация специализации шаблона функции - PullRequest
0 голосов
/ 13 ноября 2018

Я пытаюсь создать шаблон глобальной функции, специализированный для некоторых типов. Это выглядит примерно так:

A.h (основной шаблон, специализация шаблона, extern)

template <typename T> void foo() { std::cout << "default stuff" << std::endl; }
template<> void foo<int>() { std::cout << "int stuff" << std::endl; }
extern template void foo<int>();

A.cpp (явная реализация)

template void foo<int>();

B.h

void bar();

B.cpp (включая A.h)

 void bar() { foo<int>(); }

main.cpp

foo<int>();
bar();

Компилятор падает на меня: «несколько определений void foo ()». Я думал, что extern должен был позаботиться об этом. Модуль компиляции B не должен создавать экземпляр foo, а вместо этого использовать экземпляр A по ссылке раз нет? Что я тут не так делаю?

Обратите внимание, что если я не специализируюсь на foo, код компилируется просто отлично. Есть ли какой-то конфликт между специализацией функций и реализацией?

Ответы [ 2 ]

0 голосов
/ 13 ноября 2018

Поскольку ответ Брайана дает вам рабочее решение и объясняет, почему вам не нужен 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 файлах, если только ваши определения не находятся в определенном классе или пространстве имен, а не в глобальной области видимости.

0 голосов
/ 13 ноября 2018

Вам не нужно extern здесь, чтобы подавить создание экземпляров.Объявляя явную специализацию, вы уже сообщаете любому коду, который вызывает foo<int>, использовать явную специализацию, а не основной шаблон.Вместо этого вы просто хотите объявить специализацию в Ah, а затем определить ее в A.cpp:

// A.h
template <typename T> void foo() { std::cout << "default stuff" << std::endl; }
template <> void foo<int>();

// A.cpp
template <> void foo<int>() { std::cout << "int stuff" << std::endl; }

Использование extern будет уместным, если вы хотите предоставить явную реализацию первичного шаблонав некоторой единице перевода, а не явная специализация.

...