Почему шаблоны могут быть реализованы только в заголовочном файле?

Использование DateTime:

$given = new DateTime("2014-12-12 14:18:00");
echo $given->format("Y-m-d H:i:s e") . "\n"; // 2014-12-12 14:18:00 Asia/Bangkok

$given->setTimezone(new DateTimeZone("UTC"));
echo $given->format("Y-m-d H:i:s e") . "\n"; // 2014-12-12 07:18:00 UTC
1602
задан Aaron McDaid 24 February 2015 в 20:54
поделиться

6 ответов

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

Так или иначе, причина, которую приводит к сбою Ваш код, состоит в том, что при инстанцировании шаблона компилятор создает новый класс с данным аргументом шаблона. Например:

template<typename T>
struct Foo
{
    T bar;
    void doSomething(T param) {/* do stuff using T */}
};

// somewhere in a .cpp
Foo<int> f; 

При чтении этой строки, компилятор создаст новый класс (давайте назовем его FooInt), который эквивалентен следующему:

struct FooInt
{
    int bar;
    void doSomething(int param) {/* do stuff using int */}
}

, Следовательно, компилятор должен иметь доступ к реализации методов, для инстанцирования их с аргументом шаблона (в этом случае int). Если бы эти реализации не были в заголовке, то они не были бы доступны, и поэтому компилятор не смог бы инстанцировать шаблона.

общее решение А этого состоит в том, чтобы записать объявление шаблона в заголовочном файле, затем реализовать класс в файле реализации (например, .tpp) и включать этот файл реализации в конце заголовка.

// Foo.h
template <typename T>
struct Foo
{
    void doSomething(T param);
};

#include "Foo.tpp"

// Foo.tpp
template <typename T>
void Foo<T>::doSomething(T param)
{
    //implementation
}

Таким образом, реализация все еще разделяется от объявления, но доступна для компилятора.

Другое решение состоит в том, чтобы сохранить реализацию разделенной и явно инстанцировать всех шаблонных экземпляров, в которых Вы будете нуждаться:

// Foo.h

// no implementation
template <typename T> struct Foo { ... };

//----------------------------------------    
// Foo.cpp

// implementation of Foo's methods

// explicit instantiations
template class Foo<int>;
template class Foo<float>;
// You will only be able to use Foo with int or float

, Если мое объяснение не является достаточно четким, можно взглянуть на C++ супер-FAQ на этом предмете .

1433
ответ дан milleniumbug 29 August 2019 в 01:55
поделиться

Хотя в стандарте C ++ такого требования нет, некоторые компиляторы требуют, чтобы все шаблоны функций и классов были доступны в каждой используемой им единице перевода. По сути, для этих компиляторов тела шаблонных функций должны быть доступны в заголовочном файле. Повторим: это означает, что эти компиляторы не позволят им быть определенными в файлах без заголовка, таких как файлы .cpp

. Существует ключевое слово export , которое должно решить эту проблему, но это далеко не портативно.

34
ответ дан Jonny Henly 24 February 2015 в 20:54
поделиться

Шаблоны должны быть созданы компилятором, прежде чем фактически скомпилировать их в объектный код. Эта реализация может быть достигнута только в том случае, если известны аргументы шаблона. Теперь представьте сценарий, в котором функция шаблона объявлена ​​в a.h, определена в a.cpp и использована в b.cpp. Когда a.cpp компилируется, не обязательно известно, что для предстоящей компиляции b.cpp потребуется экземпляр шаблона, не говоря уже о том, какой конкретный экземпляр будет. Для большего количества заголовочных и исходных файлов ситуация может быстро усложниться.

Можно утверждать, что компиляторы можно сделать «умнее», чтобы «смотреть в будущее» для всех применений шаблона, но я уверен, что создать рекурсивные или иные сложные сценарии не составит труда. AFAIK, компиляторы не делают такой взгляд вперед. Как указал Антон, некоторые компиляторы поддерживают явные объявления экспорта экземпляров шаблона, но не все компиляторы поддерживают его (пока?).

74
ответ дан K DawG 24 February 2015 в 20:54
поделиться

Здесь много правильных ответов, но я хотел бы добавить это (для полноты):

Если вы, в нижней части файла cpp реализации, сделаете явную реализацию всех типов, с которыми будет использоваться шаблон. компоновщик сможет найти их как обычно.

Редактировать: Добавление примера явного создания шаблона. Используется после определения шаблона и определения всех функций-членов.

template class vector<int>;

Это создаст экземпляр (и, следовательно, сделает доступным для компоновщика) класс и все его функции-члены (только). Аналогичный синтаксис работает для шаблонных функций, поэтому если у вас есть перегрузки операторов, не являющихся членами, вам может потребоваться сделать то же самое для них.

Приведенный выше пример довольно бесполезен, поскольку вектор полностью определен в заголовках, за исключением случаев, когда общий включаемый файл (предварительно скомпилированный заголовок?) Использует extern template class vector<int>, чтобы не создавать его экземпляры во всех других (1000?) Файлы, которые используют вектор.

229
ответ дан Jonathan Wakely 24 February 2015 в 20:54
поделиться

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

template < typename ... >
class MyClass
{

    int myMethod()
    {
       // Not just declaration. Add method implementation here
    }
};
15
ответ дан Evan Teran 29 August 2019 в 01:55
поделиться

На самом деле до C++ 11 стандарт определил export ключевое слово, которое будет позволять объявить шаблоны в заголовочном файле и реализовать их в другом месте.

Ни один из популярных компиляторов не реализовал это ключевое слово. Единственный, о котором я знаю, является frontend, записанным проектной группой Edison, которая используется компилятором C++ Comeau. Все другие потребовали, чтобы Вы записали шаблоны в заголовочных файлах, потому что для компилятора уже нужно шаблонное определение для надлежащего инстанцирования (как другие, на которых указывают).

В результате комитет по стандарту C++ ISO решил удалить export функция шаблонов с C++ 11.

58
ответ дан DevSolar 29 August 2019 в 01:55
поделиться
Другие вопросы по тегам:

Похожие вопросы: