GCC 是正确的。
让我们从一个稍微简单的例子开始,然后证明原始示例遵循相同的模式:
template<typename T>
void bar(T const&)
{
static_assert(std::is_same<T, int>::value, "Error!");
}
int main()
{
int x = 0;
bar(x);
int const y = 0;
bar(y);
}
这里发生了什么?首先根据§14.8.2.1/3:
[...] 如果P是引用类型,则使用P所引用的类型进行类型推导。[...]
这意味着类型推导将尝试将T const与int(在情况1中)和int const(在情况2中)匹配。在第二种情况下,将int替换为T将产生完美匹配,因此很容易;在第一种情况下,我们的const妨碍了完美匹配。但这就是§14.8.2.1/4发挥作用的地方:
[...] 如果原始P是引用类型,则推导出的A(即引用所指的类型)可以比转换后的A更cv限定。 [...]
在这里,将int替换为T会给我们一个推导出的int const,它比参数x的类型int更有cv限定。但是由于上面的§14.8.2.1/4,这是可以接受的,因此即使在这种情况下,T也被推断为int。
现在让我们来处理你最初的例子(稍微调整了一下,但我们最终会回到原始版本):
template<typename T>
void bar(T const&)
{
static_assert(std::is_same<T, char[4]>::value, "Error!");
}
int main()
{
char x[] = "foo";
bar(x);
char const y[] = "foo";
bar(y);
}
除了我用
char []
替换了
int
之外,这个示例和我的第一个示例在结构上是相同的。为了看到这种等价性的原因,请考虑以下断言(在任何编译器上都不会触发,正如预期的那样):
// Does not fire
static_assert(
std::is_same<
std::add_const<char [4]>::type,
char const[4]
>::value, "Error");
C++11标准第3.9.3/2段规定了这种行为:
任何应用于数组类型的cv限定符都会影响数组元素类型,而不是数组类型(8.3.4)。
第8.3.4/1段还指定:
[...] 形式为“cv限定符序列N个T的数组”调整为“N个cv限定符序列T的数组”,对于“未知大小的T数组”的数组也是如此。可选的属性说明符序列适用于数组。 [ 示例:
typedef int A[5], AA[2][3];
typedef const A CA; // type is “array of 5 const int”
typedef const AA CAA; // type is “array of 2 array of 3 const int”
自从现在清楚这两个示例展示了相同的模式,应用相同的逻辑就是有意义的。这将引导我们通过完全相同的推理路径。
在执行类型推断时,在第一种情况下,将“T const”与“char[4]”匹配,在第二种情况下,将“T const”与“char const[4]”匹配。
在第二种情况下,“T = char [4]”产生了完美匹配,因为在替换后“T const”变为“char const[4]”。在第一种情况下,推断出的“A”再次比原始“A”更具cv限定性,因为将“char [4]”代替“T”会产生“char const[4]”。但是,这也被14.8.2.1/4所允许,因此应该将“T”推导为“char [4]”。
最后,回到您的原始示例。由于字符串文字“"str "”也具有类型“char const [4]”,因此应该将“T”推导为“char [4]”,这意味着
GCC是正确的:
template<typename T>
void foo(T const&)
{
static_assert(std::is_same<T, char[4]>::value, "Error!");
}
int main()
{
foo("str");
}
foo
。 - UmNyobeconst
应该出现在哪里(如果需要的话)。 - Matthieu M.const
应用于T
而不是它的一部分。让我们找出来... - Lightness Races in Orbit