static_assert зависит от параметра шаблона, отличного от типа (различное поведение в gcc и clang)

template <int answer> struct Hitchhiker {
  static_assert(sizeof(answer) != sizeof(answer), "Invalid answer");
};

template <> struct Hitchhiker<42> {};

При попытке отключить создание общего шаблона с помощью static_assert я обнаружил, что приведенный выше код в clang генерирует ошибку утверждения, даже если шаблон не создан, а gcc генерирует ошибку утверждения только при создании экземпляра Hitchhiker с параметром, отличным от 42.

Повозившись, я обнаружил, что это утверждение:

template <int answer> struct Hitchhiker {
  static_assert(sizeof(int[answer]) != sizeof(int[answer]), "Invalid answer");
};

template <> struct Hitchhiker<42> {};

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

Что говорит стандарт, какой компилятор прав?

g++ 4.9.2
clang++ 3.50

person bolov    schedule 06.05.2015    source источник
comment
GCC кажется правильным, поскольку объявление static_assert считается членом класса и должно возникать только при создании экземпляра класса. Кстати, можно было просто оставить первичный шаблон объявлением без определения.   -  person Lingxi    schedule 06.05.2015
comment
@Lingxi should come into being only when the class is instantiated Я не думаю, что это правда: например. static_assert(sizeof(int) != sizeof(int), "some error"); будет генерировать ошибку, когда член шаблона, даже если шаблон никогда не создавался.   -  person bolov    schedule 06.05.2015
comment
@МаркоА. Это не дубликат, потому что этот static_assert зависит от параметра шаблона.   -  person bolov    schedule 06.05.2015
comment
Возможно, связано: gcc.gnu.org/bugzilla/show_bug.cgi?id=53638   -  person Marco A.    schedule 06.05.2015
comment
Примечание: если вы хотите, чтобы Hitchhiker не создавался со случайными параметрами, вы можете просто объявить шаблон как template <int> struct Hitchhiker;. Таким образом, диагностика не настраивается, но она будет одинаково работать как на Clang, так и на gcc.   -  person Matthieu M.    schedule 06.05.2015


Ответы (2)


Цитаты, найденные @TartainLlama

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

N4296 [temp.res]/8

Это применяется сразу после определения основного шаблона (тот, в котором есть static_assert). Так что более позднюю специализацию (для 42) рассматривать нельзя, так как ее еще нет.

Следующий вопрос: если static_assert( sizeof(answer) != sizeof(answer), зависит от answer. Семантически это не так, синтаксически это так и стандартно:

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

N4296 [temp.dep]/1

Конструкция sizeof(answer) != sizeof(answer) не отличается от одного экземпляра к другому. Так что такая конструкция не зависит от параметров шаблона. Это означает, что все static_assert не зависит от параметра шаблона.

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

Причудливые попытки (например, sizeof(int[answer])!=sizeof(int[answer]) могут понравиться текущему компилятору god, но не сделают вашу программу более правильной.

Вы можете привести случай, когда компилятор вряд ли сможет вас застать за этим, но неправильность формы останется независимо от способности компилятора застать вас за этим. Как правило, С++ хочет оставить себе (и его компиляторам) свободу находить недопустимый код шаблона «до создания экземпляра»; это означает, что код шаблона должен создавать, возможно, легальный код.

Возможно, вам нужно что-то вроде =delete с прикрепленным сообщением.

person Yakk - Adam Nevraumont    schedule 06.05.2015
comment
Я не согласен с вашей интерпретацией [temp.dep]/1: я рассматриваю может как предоставление компилятору возможности для маневра, чтобы избежать фактической попытки задать вопрос о том, является ли конструкция действительно будет иметь различную семантику в том или ином экземпляре, и вместо этого пусть компилятор решит, что если есть шанс, что она может иметь различную семантику (в ней упоминаются параметры шаблона), то она считается зависимой. Я считаю, что эта свобода действий чрезвычайно важна, поскольку выражения могут быть произвольно запутанными и, таким образом, определять, возможна ли конкретизация или нет. - person Matthieu M.; 06.05.2015
comment
@MatthieuM. Он может быть произвольно запутанным - поэтому он плохо сформирован, диагностика не требуется. Однако я не уверен, что конкретный отрывок здесь уместен - гипотетическая конкретизация, по-видимому, относится конкретно к неполным типам/определениям (см. примечание). - person Barry; 06.05.2015
comment
@MatthieuM. Проблема в данном случае в том, что с одной стороны у нас программа в порядке, а с другой - программа плохо определена, диагностика не требуется. Один из способов лечения плохо сформированного состояния, не требующего диагностики, состоит в том, чтобы скомпилировать его в качественную программу. Если компилятор может доказать, что sizeof(answer)!=sizeof(answer) не является зависимым, он может затем оценить его перед созданием экземпляра и найти ошибки и (диагностировать) (высокий QOI) или (задействовать произвольное поведение) (грубый QOI). Если не получается, что это не зависит, ничто в моей логике не требует сбоя сборки? - person Yakk - Adam Nevraumont; 06.05.2015
comment
@Yakk: О, я полностью согласен с большей частью вашего ответа, а также с частью о компиляции или диагностике проблемы QOI. У меня сложилось впечатление, однако, чем когда вы сказали Так что такая конструкция не зависит от параметров шаблона вы имели в виду ее как единственно возможный исход, который не объясняет разницу в поведении. Может быть, уточнение этого предложения, чтобы объяснить, что компилятор может (или нет) понять, что конструкция не зависит от параметров шаблона, сделает вашу точку зрения более ясной? - person Matthieu M.; 06.05.2015
comment
Существует полный набор правил, позволяющих решить, зависит ли выражение, подобное sizeof(answer), от типа или от значения. Спойлер: ни то, ни другое. - person T.C.; 09.05.2015
comment
@Yakk-AdamNevraumont Если не получается, что это не зависит, ничто в моей логике не требует сбоя сборки - но не означает ли это, что ваша логика неверна, поскольку она (компилятор) тогда имела бы предположить, что он является зависимым, следовательно, не имеет неправильного формата, поэтому требуется ошибка времени компиляции из-за нарушенного статического утверждения? - person philipp2100; 04.07.2020

Оба компилятора правы. Из [temp.res]/8:

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

Не существует действительной специализации, которую можно создать из основного шаблона Hitchhiker, поэтому он имеет неправильный формат, и диагностика не требуется. clang все равно выбирает диагностику.

Если вы хотите разрешить только 42, просто не определяйте общий шаблон:

template <int > struct Hitchhiker;
template <> struct Hitchhiker<42> {};
person Barry    schedule 06.05.2015
comment
Но у шаблона есть допустимая специализация. - person Lingxi; 06.05.2015
comment
существует допустимая специализация шаблона. - person bolov; 06.05.2015
comment
Нет, нет. Нет answer, для которого sizeof(answer) != sizeof(answer). - person Barry; 06.05.2015
comment
Вы говорите об основном шаблоне, который не имеет отношения к явной специализации Hitchhiker<42>. - person Lingxi; 06.05.2015
comment
в специализации нет члена static_assert, поэтому это допустимая специализация - person bolov; 06.05.2015
comment
Я думаю, Барри прав. Для основного шаблона невозможно создать допустимую специализацию, поэтому он диагностируется во время определения. Это разрешено в стандарте в каком-то пункте, который я не успел найти. - person 0x499602D2; 06.05.2015
comment
Hitchhiker<42> не является специализацией, созданной для шаблона Hitchhiker<int >. - person Barry; 06.05.2015
comment
@ 0x49 Лучшее чтение чайного листа, которое я могу придумать, - это частичная специализация, а не специализация. Каждая частичная специализация является шаблоном, как и основной шаблон. Чтобы шаблон имел действительную специализацию, это означает, что существует некоторый набор параметров шаблона, для которых шаблон действителен. Термин «специализация» здесь не относится к «частичной специализации», это разные термины. Хотя вы считаете Hitchhiker<42> полной специализацией, в стандарте она называется частичной специализацией, но это аргумент на очень шаткой почве. - person Yakk - Adam Nevraumont; 06.05.2015
comment
Еще одна цитата ниже, которая кажется применимой: если гипотетическая реализация шаблона сразу после его определения будет неправильно сформирована из-за конструкции, которая не зависит от параметра шаблона, программа неправильно сформирована; диагностика не требуется - person TartanLlama; 06.05.2015
comment
@Tart Проблема в том, что sizeof(answer) != sizeof(answer) - это конструкция, которая зависит от answer (тривиально, скучно, но все же зависит от него)? В основном false, но false явно не зависит от answer, а answer != answer зависит (синтаксически). - person Yakk - Adam Nevraumont; 06.05.2015
comment
@Yakk Да, я тоже так думал, и ты, скорее всего, прав. Но я мог видеть допустимый аргумент, что выражение оценивает во время компиляции одинаково независимо от параметра, поэтому значение не зависит от параметра. - person TartanLlama; 06.05.2015
comment
Из [temp.dep] Внутри шаблона некоторые конструкции имеют семантику, которая может отличаться от одного экземпляра к другому. Такая конструкция зависит от параметров шаблона. Семантика здесь не отличается от одного экземпляра к другому, поэтому можно утверждать, что конструкция не зависит от параметра. - person TartanLlama; 06.05.2015
comment
@TartanLlama выглядит как ответ. Запишите это! - person Yakk - Adam Nevraumont; 06.05.2015
comment
Цитируемый отрывок, однако, не требует независимого выражения. Если компилятор может доказать, что экземпляр шаблона не может быть создан, он может выдать диагностику, даже если причиной является зависимое выражение или тип. Очевидно, что диагностика не может требоваться из-за теорем Гёделя о неполноте. Существуют более сложные обходные пути для static_assert(false, ...) в шаблоне, который не следует создавать (путем создания зависимого выражения), но хотя они и успокаивают лязг, их недостаточно для того, чтобы программа была правильно сформирована. - person Arne Vogel; 06.05.2015
comment
Интересно, что формулировка меняется для [temp.res]/8. Раньше было Если для шаблона определение не может быть создана действительная специализация, и этот шаблон не создан... Определение слова было удалено. - person Barry; 06.05.2015