2008-11-10 2 views

ответ

22

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

// ORIGINAL version of xyz.h 
template <typename T> 
struct xyz 
{ 
    xyz(); 
    ~xyz(); 
}; 

НЕ буду удовлетворен этими определениями CT и dtors:

// ORIGINAL version of xyz.cpp 
#include "xyz.h" 

template <typename T> 
xyz<T>::xyz() {} 

template <typename T> 
xyz<T>::~xyz() {} 

, потому что с помощью его:

// main.cpp 
#include "xyz.h" 

int main() 
{ 
    xyz<int> xyzint; 

    return 0; 
} 

приведет к ошибке. Например, с Comeau C++ вы получите:

C:\export>como xyz.cpp main.cpp 
C++'ing xyz.cpp... 
Comeau C/C++ 4.3.4.1 (May 29 2004 23:08:11) for MS_WINDOWS_x86 
Copyright 1988-2004 Comeau Computing. All rights reserved. 
MODE:non-strict warnings microsoft C++ 

C++'ing main.cpp... 
Comeau C/C++ 4.3.4.1 (May 29 2004 23:08:11) for MS_WINDOWS_x86 
Copyright 1988-2004 Comeau Computing. All rights reserved. 
MODE:non-strict warnings microsoft C++ 

main.obj : error LNK2001: unresolved external symbol xyz<T1>::~xyz<int>() [with T1=int] 
main.obj : error LNK2019: unresolved external symbol xyz<T1>::xyz<int>() [with T1=int] referenced in function _main 
aout.exe : fatal error LNK1120: 2 unresolved externals 

, потому что нет никакой пользы от CTOR или dtor в xyz.cpp, следовательно, нет конкретизации, что должно произойти оттуда. К лучшему или к худшему, так работают шаблоны.

Один из способов этого - явно запросить экземпляр xyz, в этом примере xyz<int>. В грубой силы усилий, это может быть добавлено к xyz.cpp путем добавления этой строки в конце его:

template xyz<int>; 

, который запрашивает, что (все) xyz<int> быть создан. Однако это не в том месте, потому что это означает, что каждый раз, когда возникает новый тип xyz, необходимо изменить файл реализации xyz.cpp. Менее навязчивый способ избежать этого файла, чтобы создать другой:

// xyztir.cpp 
#include "xyz.cpp" // .cpp file!!!, not .h file!! 

template xyz<int>; 

Это еще несколько болезненным, потому что она по-прежнему требует ручного вмешательства каждый новый хуг принесенное вперед. В нетривиальной программе это может быть необоснованным спросом на обслуживание.

Так вместо этого, другой способ решения этой проблемы является #include "xyz.cpp" в конце xyz.h:

// xyz.h 

// ... previous content of xyz.h ... 

#include "xyz.cpp" 

Вы, конечно, может буквально принести (вырезать и вставить его) содержимое xyz.cpp к конец xyz.h, следовательно, избавиться от xyz.cpp; это вопрос организации файлов, и в конечном итоге результаты предварительной обработки будут одинаковыми, поскольку тела ctor и dtor будут в заголовке и, следовательно, включены в любой запрос компиляции, так как это будет использовать соответствующий заголовок. В любом случае, это имеет побочный эффект, который теперь каждый шаблон находится в вашем файле заголовка. Это может замедлить компиляцию, и это может привести к раздуванию кода. Один из способов приблизиться к последнему - объявить рассматриваемые функции, в этом случае ctor и dtor, как inline, так что это потребует от вас изменить xyz.cpp в примере выполнения.

В некоторых случаях некоторые компиляторы также требуют, чтобы некоторые функции были определены внутри класса, а не за пределами одного, поэтому в случае этих компиляторов необходимо будет изменить настройку выше. Обратите внимание, что это проблема компилятора, а не стандарт Standard C++, поэтому не все компиляторы требуют этого. Например, Comeau C++ не делает этого и не должен. За дополнительной информацией о нашей текущей настройке обращайтесь к http://www.comeaucomputing.com/4.0/docs/userman/ati.html. Короче говоря, Comeau C++ поддерживает многие модели, в том числе те, которые близки к тому, что намерения ключевого слова экспорта (как расширение), а также даже поддерживают сам экспорт.

И, наконец, обратите внимание, что ключевое слово экспорта C++ предназначено для устранения исходного вопроса. Однако в настоящее время Comeau C++ является единственным компилятором, который публикуется для поддержки экспорта. См. http://www.comeaucomputing.com/4.0/docs/userman/export.html и http://www.comeaucomputing.com/4.3.0/minor/win95+/43stuff.txt для некоторых деталей. Надеюсь, что другие компиляторы достигнут соответствия стандарту C++, эта ситуация изменится.В приведенном выше примере, с помощью экспорта означает возврат к исходному коду, который произвел ошибки компоновщика, и внесение изменений: объявить шаблон в xyz.h с экспортом ключевым словом:

// xyz.h 

export 
// ... ORIGINAL contents of xyz.h ... 

В CTOR и dtor в А. cpp будет экспортироваться просто в силу #includeing xyz.h, который он уже делает. Таким образом, в этом случае вам не нужен xyztir.cpp или запрос на создание экземпляра в конце xyz.cpp, и вам не нужно вводить ctor или dtor вручную в xyz.h. В командной строке, показанной ранее, возможно, что компилятор сделает все за вас автоматически.

6

См this объяснение для его использования

Немало компиляторы не поддерживают это либо потому, что это слишком новый, или в случае НКУ - потому что они disaprove.

В этом сообщении описывается стандартная поддержка многих компиляторов. Visual Studio support for new C/C++ standards?

+1

Это не слишком новое, ему 10 лет, как и другие функции в стандарте C++ 98! : D Более того, для его реализации требуется редизайн компилятора, и они не считают, что это того стоит. – KTC 2008-11-11 08:06:40

+3

Это делает компоновщика сложным, особенно если вы хотите сделать много умной оптимизации всей программы на этапе ссылки. – 2008-11-11 19:39:04

1

Единственные компиляторов, которые поддерживают экспортируемых шаблонов в данный момент (насколько я знаю) являются Comeau, тот, который поставляется вместе с Borland C++ Builder X, но не в настоящее время C++ Builder и Intel (по крайней мере, неофициально, если не официально, не уверен).

4

Проще говоря:

export позволяет отделить декларацию (т.е. заголовок.) Из определения (т.е. код.), Когда вы пишете шаблонные классы. Если export не поддерживается вашим компилятором, вам необходимо поместить объявление и определение в одном месте.

5

См. here и here для лечения Херба Саттера.

В принципе: экспорт был реализован только в one компиляторе. В этой реализации экспорт фактически увеличивает связь между определением шаблона и декларацией, в то время как единственное, что вводит экспорт, - это уменьшить эту связь.

Вот почему большинство компиляторов не беспокоят. Я бы подумал, что они просто удалили бы экспорт из языка в C++ 0x, но я не думаю, что они это сделали. Возможно, когда-нибудь будет хороший способ реализовать экспорт, который имеет намеренное использование.

+1

Herb Sutter - сотрудник Microsoft. Он попытался удалить экспорт из C++ 0x и не удалось. Поэтому его мнение может быть несколько предвзятым. Раскрытие информации: Я отклонил его предложение по ИСО. – MSalters 2008-11-11 13:45:49

4

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

Компания «Комо» представила ее первой, около 5 лет назад IIRC. Он работал очень хорошо на первой бета-версии, которую я получил. Даже те тесты, как A < 2> с использованием B < 2> с использованием A < 1> с использованием B < 1> с использованием A < 0>, работал, если шаблоны A и B были выполнены из разных ТУ. Конечно, компоновщик неоднократно вызывал компилятор, но все поиски имен работали нормально. Активация A < 1> найденные имена от A.cpp, которые были невидимы в B.cpp.

Смежные вопросы