Явная реализация шаблона C ++ с помощью clang - PullRequest
3 голосов
/ 14 февраля 2020

Примечание: Несколько связанных вопросов (например, этот ) в конечном итоге были отмечены как дубликаты этого вопроса . Я знаю этот конкретный вопрос и следую за решением в соответствующих ответах. Однако разные компиляторы приводят к разному поведению, и я не знаю почему.

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

#ifndef MY_STACK
#define MY_STACK

template<class T>
class stack
{
public:
    stack();
};

#endif

, а его реализация находится в соответствующем stack.tpp файле

#ifndef MY_STACK_TPP
#define MY_STACK_TPP

#include <iostream>

template<class T>
stack<T>::stack()
{
    std::cout << "My stack constructor!" << std::endl;
}

#endif

Поскольку я хотел бы предложить поддержку только для Для определенных аргументов шаблона my stack.cpp создает следующие явные экземпляры шаблона:

#include "stack.hpp"

template class stack<double>;
template class stack<char>;

#include "stack.tpp"

Компилируется с g ++ и clang ++, но существуют различия в символах результирующей разделяемой библиотеки:

g++ -std=c++11 -c stack.cpp -o stack.so
nm -C stack.so | grep stack
0000000000000049 t _GLOBAL__sub_I_stack.cpp
0000000000000000 W stack<char>::stack()
0000000000000000 W stack<char>::stack()
0000000000000000 n stack<char>::stack()
0000000000000000 W stack<double>::stack()
0000000000000000 W stack<double>::stack()
0000000000000000 n stack<double>::stack()

vs.

clang++-7 -std=c++11 -c stack.cpp -o stack.so
nm -C stack.so | grep stack
0000000000000050 t _GLOBAL__sub_I_stack.cpp

В моем приложении конструктор такого явно созданного экземпляра класса не найден при использовании clang ++, но он прекрасно работает с g ++. Я полагаю, что это основание c MWE дает причину. Может кто-нибудь сказать мне, как я могу получить символы конструктора для моего шаблона класса, используя clang ++?

1 Ответ

3 голосов
/ 14 февраля 2020

Эта программа правильно сформирована

Цитирование [temp.explicit] / 1 [ выделение мое]:

A Класс, функция, переменная или специализация шаблона члена могут быть явно созданы из его шаблона. функция-член , класс-член или stati c элемент-член шаблона класса может быть явно создан из определения члена, связанного с его шаблоном класса . [..]

И, цитируя [temp.explicit] / 9 [ выделение мое]:

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

Таким образом, в примере OP явное определение экземпляра stack<T> не будет включать явное определение экземпляра конструктора, так как явное определение экземпляра stack<T> помещено до с предоставлением определения его конструктора через .tpp include.

Цитирование [temp.point] / 8 [ выделение mine]:

Специализация для шаблона функции, шаблона функции-члена или функции-члена или stati c члена данных элемента шаблон класса может иметь мю В нескольких точках инстанции в единице перевода и в дополнение к точкам инстанцирования, описанным выше, для любой такой специализации, которая имеет точку инстанцирования в единице перевода, также считается конец единицы перевода точка создания экземпляра. Специализация для шаблона класса имеет не более одной точки создания экземпляра в единице перевода. Специализация для любого шаблона может иметь точки реализации в нескольких единицах перевода. Если две разные точки инстанцирования придают специализации шаблона разные значения в соответствии с правилом единого определения, программа некорректна, диагностика не требуется c не требуется.

Таким образом , для случаев, когда stack.cpp включает в себя две разные точки инстанцирования, и где один до и один после включения stack.tpp, то программа плохо сформирована.

Однако здесь она становится немного сложно, так как точки реализации зависят от того, как используется шаблон класса и его функция-член (/ конструктор). Как указано в приведенном выше [temp.explicit] / 9 , явное создание экземпляра stack<T> не приведет к явному определению экземпляра его конструктора, и вместо этого нам нужно будет вернуться к [temp.point] для подробностей, в частности пунктов 1, 2 и 4, о , когда его контекст использования приведет к точке создания до включения stack.tpp.

Автономный пример в вопросе не охватывается ни одним из этих случаев, и поэтому программа us не плохо сформирована.

G CC vs clang: казалось бы, другое поведение экземпляра ?

Может кто-нибудь сказать мне, как я могу получить символы конструктора для моего шаблона класса, используя clang ++?

Поскольку конструктор никогда не используется, он никогда не должен (нужно ) создается вообще, но кажется, что (из дампа символа OP) G CC делает это в любом случае (что не является недопустимым), а clang - нет. Если кто-то использует / ссылается на конструктор после включения track.tpp, то и G CC, и clang естественным образом его создают (для конкретной используемой специализации), поскольку они тогда требуются сделать это.

...