2014-02-03 2 views
2

Резюме
Этот вопрос касается достижения отдельной компиляции экземпляра экземпляра класса в нескольких разных единицах перевода.Отдельная компиляция и шаблон с явным экземпляром

Вопрос
Для классов нешаблонном можно поместить определения в несколько файлов .cpp и скомпилировать их отдельно. Например:

файл хиджры:

class A { 
public: 
    void func1(); 
    void func2(); 
    void func3() { /* defined in class declaration */} 
} 

файл A1.cpp:

void A::func1() { /* do smth */ } 

файл A2.cpp:

void A::func2() { /* do smth else */ } 

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

файл TA.h:

template <typename T> 
class TA { 
public: 
    void func1(); 
    void func2(); 
    void func3() { /* defined in class declaration */} 
} 

файл TA1.cpp:

template <typename T> 
void TA<T>::func1() { /* do smth */ } 
template class TA<sometype>; 

файл TA2.cpp:

template <typename T> 
void TA<T>::func2() { /* do smth else */ } 
template class TA<sometype>; 

Он работает с clang и GCC в Linux, но не работает с GCC на Mac во время связывания во время ошибки повторяющихся символов (в этом примере из-за func3, который получил экземпляр как в TA1.cpp, так и в TA2.cpp).

Потом я наткнулся на эту фразу в стандарте:

C++ 11.14.7, пункт 5:
Для данного шаблона и заданного набора шаблонов-аргументов,
- явный конкретизации де определению должны появляться не чаще одного раза в программе,
- ...

означает ли это, что отдельный сборник шаблонов классов не представляется возможным (не допускается) даже при использовании экс (очевидно, это невозможно с неявным экземпляром)?

PS Мне все равно, так как у меня есть мой ответ, но тот, кто думает, что здесь отвечает https://stackoverflow.com/questions/495021/why-can-templates-only-be-implemented-in-the-header-file, ошибается.

+2

Очевидно, что это необязательно с неявным экземпляром. –

+0

@LightnessRacesinOrbit, как нужна отдельная компиляция с неявным экземпляром? – Vayun

+0

@Vayun: неявное создание шаблона происходит при использовании. Когда вы используете шаблон в двух или более TU, он был создан компилятором для каждого TU. Компилятор не может знать, что какой-то другой TU уже создавал его. Таким образом, это должно быть законным. –

ответ

1

После очередного взгляда на стандарт, мне кажется, что единственно разумным вариантом является использование единого явного экземпляра экземпляра шаблона в сочетании с явными экземплярами функций-членов небольшого числа «трудных» функций.

Это (согласно 14.7.2p9) будет создавать экземпляр класса и всех членов, которые были определены до этой точки (которые должны включать в себя все , за исключением «трудных» членов). Затем эти выбранные элементы могут быть явно созданы в других единицах перевода, содержащих их определения.

Это сделало бы мой пример выглядеть, как показано ниже (при условии, что TA1.cpp содержит простые функции и «трудного» функцию только в TA является func2)

файл TA1.cpp:

template <typename T> 
void TA<T>::func1() { /* "simple" function definition */ } 

template class TA<sometype>; /* expl. inst. of class */ 

файл TA2.cpp:

template <typename T> 
void TA<T>::func2() { /* "difficult" function definition */ } 

template void TA<sometype>::func2(); /* expl. inst. of member */ 

Этот метод требует от нас описания явного определения экземпляра для каждой «трудной» функции, которая является утомительной, но также заставляет нас дважды подумать, хотим ли мы сохранить ее отдельно или нет.

Отказ

Когда это может быть полезно? Не часто. Как упоминалось выше, не рекомендуется разделять определения классов по нескольким файлам. В моем конкретном случае «сложные» функции содержат сложные математические операции над экземплярами нетривиальных классов. Шаблоны C++ не известны быстрой скоростью компиляции, но в этом случае это было невыносимо. Эти функции называют друг друга, который отправляет компилятор на длительный и многопользовательский путь расширения/встраивания перегруженных операторов/шаблонов/etc для оптимизации всего, что он видит, с почти нулевым улучшением, но создание компиляции длится часами. Этот трюк выделения некоторых функций в отдельных файлах ускоряет сбор 20 раз (и позволяет также распараллелить его).

1

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

+0

Так как я могу скомпилировать TA :: func1 в одной единицы перевода и TA :: func2 в другой? – Vayun

+0

@Vayun: Вы можете вызвать неявное создание экземпляра этого члена, но я бы этого избежал. Определение частей класса в разных единицах перевода рано или поздно будет проблематичным. Что касается того, как запускать неявное создание, просто возьмите адрес: '/ * конец файла */void (Шаблон :: *)() = & Шаблон :: func1;' - Снова я бы ** не ** сделай это. –

+0

Определение частей класса в разных единицах перевода вполне нормально и не вызывает проблем для классов без шаблонов. В любом случае, вы отвечаете, было полезно, но я буду использовать явное создание экземпляров некоторых элементов в дополнение к явному экземпляру класса. – Vayun

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