static_assert в неинициализированном классе шаблона с действительной специализацией

Мой вопрос в том, действителен ли следующий код:

template<int i> class Class
{
    static_assert(sizeof(i) == 0, "Class instantiated with i != 1");
};

template<> class Class<1> {};

Этот фрагмент компилируется с g++. Но clang++ попал в ловушку static_assert:

error: static_assert failed "Class instantiated with non-int type"

Шаблон, который использует тип вместо int как

template<typename T> class Class
{
    static_assert(sizeof(T) == 0, "Class instantiated with non-int type");
};

template<> class Class<int> {};

принимается обоими компиляторами. Точно такой же шаблон применяется к шаблонам функций.

Я нашел open-std.org::Независимый static_assert-declarations, но это, похоже, не применимо, потому что мой static_assert зависит от параметра шаблона.

Вы можете проверить описанное поведение на godbolt.org.

EDIT: Как отмечает Йохан Лундберг в комментарии, мой вопрос неверен. Действительно, sizeof(i) не зависит от параметра шаблона. Также совершенно прав Р.Саху: Гораздо разумнее было бы утверждать i != 1. Для этого снова оба компилятора принимают код.

Однако все еще верхний пример компилируется с g++. Как open-std.org::Независимый static_assert -declarations относится к этому случаю (я еще раз извиняюсь за неправильный вопрос в этом отношении): действительно ли g++ неправильно компилирует код без ошибок?


person marlam    schedule 07.04.2017    source источник
comment
Я не думаю, что вы правы, потому что мой static_assert зависит от параметра шаблона. sizeof(i) не зависит от value i.   -  person Johan Lundberg    schedule 08.04.2017
comment
Почему вы используете sizeof в первом примере? i имеет тип int, а sizeof(i) совпадает с sizeof(int). Возможно, в утверждении вы хотите использовать i напрямую.   -  person Fabio    schedule 08.04.2017
comment
Вы случайно не хотели использовать static_assert(i != 1, "Class instantiated with i != 1");?   -  person R Sahu    schedule 08.04.2017
comment
Я думаю, что оба они неверны, поскольку недействительные экземпляры недействительны.   -  person Jarod42    schedule 08.04.2017
comment
@Jarod42: Вы уверены, что даже второй фрагмент с typename T неверен? Потому что здесь static_assert зависит от параметра шаблона (даже если он всегда равен false). Почему это должно быть неправильно?   -  person marlam    schedule 08.04.2017
comment
Для второго примера: 14.6/8 Если для шаблона не может быть сгенерирована допустимая специализация, и этот шаблон не создан, шаблон неправильно сформирован, диагностика не требуется.   -  person aschepler    schedule 08.04.2017
comment
@aschepler: я знаю, что это основано на мнении, но каков правильный (не неправильный) способ реализации такой схемы (не по умолчанию, а с несколькими специализациями)? Следует ли только объявить класс без какого-либо определения, такого как template<typename T> class Class;, и предложить определение только для допустимых специализаций? Компилятор наверняка пожалуется, если не будет доступной допустимой специализации. Или есть еще один момент, который следует учитывать?   -  person marlam    schedule 08.04.2017
comment
Стандарт не определяет основной шаблон, например std::function<T>. Вы также можете определить template<int N> struct always_false : public std::false_type {}; и выполнить static_assert(always_false<i>::value, "My error");   -  person aschepler    schedule 08.04.2017
comment
Это обсуждение привело меня к ошибке gcc 80368 gcc.gnu.org/bugzilla/show_bug .cgi?id=80368   -  person aschepler    schedule 08.04.2017


Ответы (2)


clang++ прав, чтобы отклонить ваш код, но g++ не ошибается, если не поймает ошибку; это не требующая диагностики ситуация.

Стандарт строго определяет выражения в шаблоне как зависящие от типа и/или значения. Учитывая template<int i>, i зависит от значения, но не от типа.

[14.6.2.2/4]: выражения следующих форм никогда не зависят от типа (поскольку тип выражения не может быть зависимым):

  • ...
  • sizeof унарное-выражение
  • ...

[14.6.2.3/2]: выражения следующей формы зависят от значения, если унарное-выражение или выражение зависит от типа или идентификатор типа зависит:

  • sizeof унарное-выражение
  • ...

Так что sizeof(i) не зависит.

Наконец, 14.6/8 говорит:

Если гипотетическая реализация шаблона сразу после его определения будет неправильно сформирована из-за конструкции, не зависящей от параметра шаблона, программа будет неправильно сформирована; диагностика не требуется.

person aschepler    schedule 07.04.2017

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

#include <type_traits>

template<typename T> class Class
{
    static_assert(std::is_same<T, int>::value, "Class instantiated with non-int type");


};

Во-вторых, следующая строка может делать не то, что вы думаете:

template<> class Class<int> {};

На самом деле здесь вы создаете совершенно новую специализацию, которая не имеет ничего общего с шаблоном специализацииClass<T> по умолчанию.

Удаление специализации для int и создание экземпляра не-int класса специализации по умолчанию приводит к ошибке в обоих компиляторах, как и должно быть:

int main()
{
    Class<int> ci;
    Class<float> cf;
}

пример ошибки:

<source>: In instantiation of 'class Class<float>':
<source>:14:18:   required from here
<source>:5:5: error: static assertion failed: Class instantiated with non-int type
     static_assert(std::is_same<T, int>::value, "Class instantiated with non-int type");
     ^~~~~~~~~~~~~
person Richard Hodges    schedule 07.04.2017
comment
Спасибо за Ваш ответ. Но я думаю, что ваша идея несколько отличается от моей. Я думаю о классе, который не имеет (полезной) реализации по умолчанию, но предлагает несколько (не одну) специализаций. Благодаря этому я могу выбрать подходящую реализацию во время компиляции. static_assert предоставляет только читаемое сообщение об ошибке. - person marlam; 08.04.2017
comment
@marlam верно ... но помните, что каждая специализация, которую вы определяете, представляет собой совершенно новый класс, в котором нет static_assert. Вы определяете Class<int>, а не создаете его экземпляр. - person Richard Hodges; 08.04.2017
comment
Правильно, это то, что я имел в виду. Это противоречило бы идее, если бы static_assert был в каждой специализации. Тем не менее, мы все еще с вопросом, почему g++ принимает первый фрагмент (см. редактирование в вопросе). - person marlam; 08.04.2017