在C ++ 11中,模板中static_assert的操作是否应取决于该模板是否已实例化?例如,使用以下代码
template <int I>
void sa() { static_assert(0,"Hello."); }
int main(int argc, char *argv[]) { return 0; }
GCC 4.5.0将无法断言,并产生“Hello”。信息。 另一方面,Digital Mars Compiler版本8.42n没有给出消息。
答案 0 :(得分:50)
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
是一个类模板(可以将其专门化,因此编译器不可能已经知道布尔值)。
答案 1 :(得分:8)
我相信编译器完全有权扩展任何不依赖于模板参数的静态断言而不需要实例化 - 但我不认为这是必需的。还要记住,不同的标准草案可能会对何时发生这种情况有不同的规定。
答案 2 :(得分:4)
C ++ 0x草案(N3242)在14.6p8中说:
“如果没有有效的专业化 为模板定义生成, 并且该模板未实例化, 模板定义是不正确的, 无需诊断。“
C ++ 03标准中出现了相同的单词。
对于该问题的示例,不能对此模板进行有效的实例化,因此引用的措辞适用。
由于不需要诊断,如果未实例化模板,编译器可以编译程序。当然,如果它被实例化,那么程序就会形成错误,需要诊断。
答案 3 :(得分:3)
我使用了一个辅助函数来使false依赖于模板参数:
template<typename T>
bool dependentFalse<T>()
{
return false;
}
template<typename T>
Foo foo()
{
static_assert(dependentFalse<T>(), "this template shouldn't be instantiated");
}
答案 4 :(得分:2)
这是C ++标准中的14.6.2
部分。
您的static_assert
与最初解析模板时支持绑定非依赖名称相关。 Digital Mars Compiler目前不支持绑定非依赖名称。 GCC 4.5.0确实支持绑定非依赖名称。
如果表达式不依赖于模板参数,那么在最初解析模板时,这种表达式是已知的。编译器必须显示错误消息。 GCC 4.5.0做到了。
将0
中的static_assert
替换为I*I < 0
,以获取示例,并获得相关名称。仅针对未实例化模板的情况将显示错误消息。
答案 5 :(得分:-1)
该程序生成错误:
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
,如果它已经改变,它就不能像它应该的那样进行编译。