#c #language-lawyer #function-templates #non-type-template-parameter #implicit-instantiation
Вопрос:
Я хочу написать шаблон класса M
, который принимает неполный тип C
в качестве параметра шаблона. Но я также хочу C
, чтобы у меня были некоторые черты, когда это в конечном итоге будет определено.
Гарантируется ли этот код
- для компиляции, если определено(ФЛАГ),
- и не выполнить компиляцию, если !определено(ФЛАГ)?
template <auto> struct Dummy {};
template <typename C>
void check()
{
static_assert(std::is_trivial_v<C>);
}
template <typename C>
struct M : Dummy<amp;check<C>>
{
//static_assert(std::is_trivial_v<C>);//error: incomplete type
C * p;
};
struct Test;
M<Test> m;
int main()
{
return 0;
}
#if defined(FLAG)
struct Test {};
#else
struct Test { std::string non_trivial_member; };
#endif
Комментарии:
1. Я подозреваю,
M<Test>
что ваш код уже плохо сформирован ndr. Но я не уверен. У меня есть смутное воспоминание о предложении, включающем в момент создания экземпляра и в конце блока компиляции, предназначенное для того, чтобы дать компилятору свободу отложить создание экземпляраCheck
тела, но требующее, чтобы любое место давало тот же результат.2. @Yakk-AdamNevraumont: Насколько я помню, правила различны для классов и функций. класс создается только один раз, в то время как создание экземпляра функции должно «уважать» ODR (и поэтому быть идентичным для каждого POI (и EOF-возможный POI)).
3. так что проблема была бы с
check<C>
, но нетM<C>
.4. @Jarod42 Но POI
check<C>
onstruct M : Dummy<amp;check<C>>
-этоM<Test>
линия, нет? Т. е., еслиTest
бы где-то было определено послеM
,M<Test>
все было бы в порядке (все остальное-безумие).5. @Yakk-Адамневромонт: Я бы сказал
M<Test>
, экземплярыcheck<Test>
, иcheck<Test>
снова для EOF. ТакM<Test>
создается один раз с неполнымTest
и проверяется дважды, один раз с неполнымTest
и один раз с полнымTest
.
Ответ №1:
Точка создания экземпляра [временная точка] (17.7.4.1/8)
Специализация для шаблона функции, шаблона функции-члена или функции-члена или элемента статических данных шаблона класса может иметь несколько точек создания экземпляров в единице перевода, и в дополнение к точкам создания экземпляров, описанным выше, для любой такой специализации, имеющей точку создания экземпляра в единице перевода, конец единицы перевода также считается точкой создания экземпляра. Специализация для шаблона класса имеет не более одной точки создания экземпляра в единице перевода. Специализация для любого шаблона может иметь точки создания экземпляров в нескольких единицах перевода. Если две разные точки создания экземпляра придают шаблону разные значения в соответствии с правилом одного определения (6.2), программа плохо сформирована, диагностика не требуется.
Во-первых, обратите внимание, что основной шаблон, используемый paa0ss, является специализацией в стандарте speak. Программисты на C используют его иначе, чем стандарт, по моему опыту.
Во-вторых, check<Test>
в вашей программе есть две точки создания экземпляра; один раз в
M<Test> m;
и один раз в конце блока перевода.
Значение check<Test>
at M<Test> m
отличается от значения check<Test>
в конце единицы перевода. В одном месте Test
оно неполное, в другом-полное. Тело check<Test>
определенно имеет другое значение.
Таким образом, ваша программа плохо сформирована, диагностика не требуется. В вашем случае плохо сформированная программа делает то, что вы хотите, но она может (в соответствии со стандартом) скомпилироваться вообще во что угодно или не скомпилироваться.
Я подозреваю, что причина этого правила заключается в том, чтобы предоставить компилятору свободу создания экземпляра check
либо немедленно, либо отложить его на более поздний срок. Вам не разрешается полагаться на то, в каком из двух мест он на самом деле создает экземпляр тела check
.