С++ 11 static_assert и создание шаблона

В С++ 11, если операция static_assert в шаблоне зависит от того, был ли этот экземпляр создан или нет? Например, со следующим кодом

template <int I>
void sa() { static_assert(0,"Hello."); }

int main(int argc, char *argv[]) { return 0; }

GCC 4.5.0 не выполнит это утверждение и произведет "Привет". сообщение. С другой стороны, компилятор Digital Mars компилятор версии 8.42n не дает сообщений.

Ответ 1

GCC верен, а другой компилятор тоже прав. См. 14.6p8 в спецификации

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

Поэтому компилятор может отклонить следующие

template<typename T>
void f() {
  static_assert(0, "may trigger immediately!");
  static_assert(sizeof(T) == 0, "may trigger immediately!");
}

Если вы хотите пойти в безопасное место, вам нужно организовать его, чтобы компилятор не смог узнать, пока не будет установлено, будет ли логическое выражение истинным или ложным. Например, получите значение getvalue<T>::value, при этом getvalue является шаблоном класса (можно было бы его специализировать, поэтому компилятор не может знать булевское значение уже).

Ответ 2

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

Ответ 3

Проект С++ 0x (N3242) говорит в 14.6p8:

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

Эти же слова появляются в стандарте С++ 03.

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

Поскольку никакой диагностики не требуется, компилятор может скомпилировать программу, если шаблон не создан. Конечно, если он создается, программа плохо сформирована, и требуется диагностика.

Ответ 4

Я использовал вспомогательную функцию, чтобы сделать ложную зависимость от параметра шаблона:

template<typename T> 
bool dependentFalse<T>()
{
    return false;
}

template<typename T>
Foo foo()
{
    static_assert(dependentFalse<T>(), "this template shouldn't be instantiated");
}

Ответ 5

Это 14.6.2 раздел в стандарте С++.

Ваш static_assert связан с поддержкой привязки не зависящих имен при первоначальном анализе шаблона. Компилятор Digital Mars Compiler в настоящее время не поддерживает привязные независимые имена. GCC 4.5.0 поддерживает привязку не зависящих имен.

Если ваше выражение не зависит от параметров шаблона, то такое выражение известно при первоначальном анализе шаблона. Компилятор должен показать сообщение об ошибке. GCC 4.5.0 делает это.

Замените 0 static_assert на static_assert на I*I < 0, для примера, и вы получите зависимое имя. Сообщение об ошибке появится только для случая незакрепленного шаблона.

Ответ 6

Эта программа генерирует ошибку:

template <int I>
void sa()
{
    static_assert(0,"Hello.");
}

template <>
void sa<0>()
{
}

int main(int argc, char *argv[]) { return 0; }

Эта программа не выполняет:

template <int I>
void sa()
{
    static_assert(I != 0,"Hello.");
}

template <>
void sa<0>()
{
}

int main(int argc, char *argv[]) { return 0; }

Это не имеет никакого смысла для этого. Поэтому я прихожу к выводу, что g++ 4.5 должен быть ошибочным, если он запускает static_assert в непроинформированном шаблоне.

И еще более тревожно, следующая программа выводит I == 1.

#include <iostream>

using ::std::cout;

template <int I>
void sa()
{
    cout << "I == " << I << '\n';
    static_assert(I != 0,"Hello.");
}

template <>
void sa<0>()
{
    cout << "I == " << 0 << '\n';
}

int main(int argc, char *argv[]) { sa<1>(); return 0; }

Это указывает на серьезную ошибку в том, как gcc обрабатывает static_assert.

Изменить: О, хорошо. В моей программе есть ошибка. Он должен читать I == 0, а не I != 0, и если он изменился, он не сможет скомпилироваться так, как должен.