Делает ли «используется в качестве параметра шаблона, не относящегося к типу», неявный экземпляр шаблона функции?

#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> on struct 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:

Из номера 4713,

Точка создания экземпляра [временная точка] (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 .