字符串文字模板 - 不同的行为编译器的
假设我们有下面的代码:字符串文字模板 - 不同的行为编译器的
template <typename T>
void foo(const T&);
int main()
{
foo("str");
}
GCC 4.7.2,铛3.2,ICC 13.0.1
未定义引用'void foo < char [4]>(char const(& )[4])”
MSVC-11.0
解析外部符号“空隙__cdecl FOO < 字符常量 [4]>(炭 常量(&)[4]) “(?? $ foo的@ $$ BY03 $$ CBD @@ YAXAAY03 $$ CBD @ Z)
通知char[4]
在第二O的第一输出和char const[4]
本安输出。
为什么?谁是对的?你能引用标准吗?
GCC是正确的;这const
在VS的模板参数列表不应该存在:进行这种替代
[C++11: 14.8.2/3]:
后,在8.3.5中描述的函数参数类型调整执行。 [示例:参数类型“void()(const int, int[5])”
变成“void(*)(int,int*)”
。 - 结束示例][注意:函数参数声明中的*限定符不影响函数 类型,但仍影响函数中函数参数变量的类型。 末端音符][实施例:template <class T> void f(T t); template <class X> void g(const X x); template <class Z> void h(Z, Z*); int main() { // #1: function type is f(int), t is non const f<int>(1); // #2: function type is f(int), t is const f<const int>(1); // #3: function type is g(int), x is const g<int>(1); // #4: function type is g(int), x is const g<const int>(1); // #5: function type is h(int, const int*) h<const int>(1,0); }
末端示例]
(实施例4是相关的。)
[C++11: 14.8.2/5]:
将所得使用替代和调整的函数类型作为f的类型模板论证扣除的联合模板。[..]
而且可能相关:
从一个函数调用推导模板参数
[C++11: 14.8.2.1/2]:
如果P
是不是引用类型:
- 如果
A
是数组类型,则使用由数组到指针标准转换(4.2)生成的指针类型来代替A
用于类型扣除;否则,- 如果
A
是函数类型,则使用由函数指针标准转换(4.3)生成的指针类型来代替类型推导的A
;否则,- 如果
A
为A
“S型的CV-限定的类型,顶层CV-预选赛类型扣除被忽略
我们都知道这一点,但它是如何适用于有关案件? – 2013-03-19 14:37:02
@JamesKanze:'const'不是函数类型的一部分,函数类型用于模板参数推导。因此,在这种情况下'T'不应该是'const',但在OP的例子中MSVC已经这样做了。 – 2013-03-19 14:37:42
我确实认为我可能会错过几个步骤。 :(想到我会把它扔到那里,尽管如此 – 2013-03-19 14:40:27
GCC是正确的。
让我们从一个稍微简单的例子开始,后来证明原来的例子遵循相同的模式:
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(由参考简称,即,类型) 可以是 更CV-合格比转化A. [...]
在这里,替换为int
T
给我们带来了推导int const
,这是更CV-合格比int
(的类型参数x
)。但由于上述第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也规定:
[...]任何类型的形式是“NT的CV-限定符-SEQ阵列”的被调整到“N CV的阵列 -qualifier-seq T“,对于”T的未知范围数组“也是类似的。可选的属性说明符seq 属于数组。 [实施例:
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-限定符-SEQ的T数组”具有CV-限定类型;见3.9.3。 - 注意]
由于现在很清楚这两个示例呈现相同的模式,因此应用相同的逻辑是有意义的。这将引导我们走完全相同的推理路径。
在执行类型扣除时,T const
在第一种情况下与char[4]
匹配,在第二种情况下与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&)
{
// Shall not fire
static_assert(std::is_same<T, char[4]>::value, "Error!");
}
int main()
{
foo("str"); // Shall not trigger the assertion
}
但为什么'std :: add_const
@interjay:请参见C++ 11标准的3.9.3/2:“[...]应用于数组类型的任何cv限定符影响数组元素类型,而不是数组类型(8.3.4)。” – 2013-03-19 23:25:18
@interjay:我在最后一次更新中对此进行了扩展 – 2013-03-19 23:29:56
两者都在抱怨没有'foo'定义.... – UmNyobe 2013-03-19 13:57:34
@UmNyobe是的,我知道,它只需要打印类型 – FrozenHeart 2013-03-19 13:59:20
@UmNyobe我的意思是什么类型应该在这种情况下 - char [4]或char const [4]? – FrozenHeart 2013-03-19 14:00:08