海合会是对的。
让我们从一个稍微简单的例子开始,稍后证明原来的例子也遵循同样的模式:
template<typename T>
void bar(T const&)
{
// Shall not fire
static_assert(std::is_same<T, int>::value, "Error!");
}
int main()
{
int x = 0;
bar(x); // 1 - Assertion won't fire
int const y = 0;
bar(y); // 2 - Assertion won't fire
}
这里发生了什么事?首先,根据第 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
forT
给了我们一个 deduced int const
,它比int
(参数的类型x
)更具 cv 限定性。但这是可以接受的,因为上面的第 14.8.2.1/4 节,所以即使在这种情况下T
也被推断为int
。
现在让我们处理您的原始示例(稍作调整,但我们最终会使用原始版本):
template<typename T>
void bar(T const&)
{
// Does not fire in GCC, fires in VC11. Who's right?
static_assert(std::is_same<T, char[4]>::value, "Error!");
}
int main()
{
char x[] = "foo";
bar(x);
char const y[] = "foo";
bar(y);
}
除了我替换int
为之外char []
,这是示例和我的第一个示例在结构上是相同的。要了解为什么这种等价性成立,请考虑下面的断言(正如预期的那样,它不会在任何编译器上触发):
// 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-qualifier-seq array of NT”都被调整为“array of N cv-qualifier-seq T”,同样适用于“array of unknown bound of 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”
—结束示例] [注意:“N cv-qualifier-seq T 的数组”具有 cv 限定类型;见 3.9.3。——尾注]
由于现在很清楚这两个示例展示了相同的模式,因此应用相同的逻辑是有意义的。这将引导我们走同样的推理路径。
在执行类型推导时,在第一种情况下T const
匹配,在第二种情况下匹配。char[4]
char const[4]
在第二种情况下,T = char[4]
产生完美匹配,因为在替换之后T const
变为。char const[4]
在第一种情况下, deducedA
再次比原始 cv 更合格A
,因为它代替char[4]
了T
yield char const[4]
。但话又说回来,这是 14.8.2.1/4 允许的,所以T
应该推导出为char[4]
.
最后,回到你原来的例子。由于字符串字面"str"
量也有 type char const[4]
,T
应该推导出为 be char [4]
, 这意味着GCC 是对的:
template<typename T>
void foo(T const&)
{
// Shall not fire
static_assert(std::is_same<T, char[4]>::value, "Error!");
}
int main()
{
foo("str"); // Shall not trigger the assertion
}