在研究this question的过程中,我发现(以前不知道)gcc和clang允许char
数组作为模板参数,如果声明为static
。例如。这段代码使用gcc和clang编译:
#include <type_traits>
template <int N, const char (&string)[N]>
auto foo()
{
if constexpr (string[0] == 'i')
return 0;
else
return 3.14f;
}
void bar()
{
static constexpr char string1[] = "int";
static constexpr char string2[] = "float";
auto i = foo<sizeof(string1), string1>();
auto f = foo<sizeof(string2), string2>();
static_assert(std::is_same_v<decltype(i), int>);
static_assert(std::is_same_v<decltype(f), float>);
}
MSVC也允许这样做。但是,要使其与MSVC一起使用,我必须在全局名称空间中声明两个字符串。然后它也可以正常工作。
所以我的问题是:标准对此有何表述?哪个编译器(如果有)是正确的?
答案 0 :(得分:11)
这是从C ++ 14到C ++ 17的更改,看起来MSVS尚未赶上。先前在[temp.arg.nontype]中,非类型参数必须为
非类型,非模板模板参数的模板参数应为以下之一:
对于整数或枚举类型的非类型模板参数,为该模板参数类型的转换后的常量表达式([expr.const]);或
非类型模板参数的名称;或
一个常量表达式([expr.const]),它指定具有静态存储持续时间和外部或内部链接的完整对象的地址,或者具有外部或内部链接的函数,包括函数模板和函数模板ID,但不包括非静态类成员,表示为&id-expression(忽略括号),其中id-expression是对象或函数的名称,但如果名称引用函数或数组,如果相应的模板参数是引用,则应省略;或
一个常量表达式,其结果为空指针值([conv.ptr]);或
一个常量表达式,其值为空成员指针值([conv.mem]);或
指向成员的指针,如[expr.unary.op]中所述;或
类型为
std::nullptr_t
的常量表达式。
强调我的
并且由于项目符号3,您不能使用块范围变量,因为每个[basic.link]/10的块范围变量都没有链接
这些规则未涵盖的名称没有链接。此外,除非另有说明,否则在块范围内声明的名称没有链接。
在C ++ 17中,此更改。 [temp.arg.nontype]现在有
非类型模板参数的模板参数应为模板参数类型的转换常量表达式。对于引用或指针类型的非类型模板参数,常量表达式的值不得引用(或对于指针类型,不得为其地址):
子对象
一个临时对象
字符串文字
Typeid表达式的结果,或
预定义的 func __变量。
现在,您可以使用块范围静态变量