Fro*_*art 12 c++ templates language-lawyer
假设我们有以下代码:
template <typename T>
void foo(const T&);
int main()
{
foo("str");
}
Run Code Online (Sandbox Code Playgroud)
gcc 4.7.2,clang 3.2,icc 13.0.1
未命名的引用`void foo < char [4]>(char const(&)[4])'
MSVC-11.0
未解析的外部符号"void __cdecl foo < char const [4]>(char const(&)[4])"(?? $ foo @ $$ BY03 $$ CBD @@ YAXAAY03 $$ CBD @ Z)
请注意char[4]
第一个输出和char const[4]
第二个输出.
为什么?谁是对的?你能引用这个标准吗?
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
}
Run Code Online (Sandbox Code Playgroud)
这里发生了什么事?首先,根据§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
,它比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);
}
Run Code Online (Sandbox Code Playgroud)
除此之外,我所替换int
用char []
,这是例子,第一个例子是在结构上是相同的.要了解为什么这个等价性成立,请考虑下面的断言(不会像预期的那样触发任何编译器):
// Does not fire
static_assert(
std::is_same<
std::add_const<char [4]>::type,
char const[4]
>::value, "Error");
Run Code Online (Sandbox Code Playgroud)
C++ 11标准规定了第3.9.3/2段中的这种行为:
应用于数组类型的任何cv限定符都会影响数组元素类型,而不是数组类型(8.3.4).
第8.3.4/1段还规定:
[...]任何类型的形式"NT的cv-qualifier-seq数组"被调整为"N cv-qualifier-seq T的数组",并且类似地用于"T的未知界限的数组".可选的attribute-specifier-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”
Run Code Online (Sandbox Code Playgroud)
-end example] [注意:"N cv-qualifier-seq T的数组"具有cv限定类型; 见3.9.3. - 尾注]
由于现在很清楚这两个示例表现出相同的模式,因此应用相同的逻辑是有意义的.这将引导我们走过同样的推理道路.
在执行类型扣除时,在第一种情况下T const
匹配,char[4]
在char const[4]
第二种情况下与之匹配.
在第二种情况下,T = char[4]
产生完美匹配,因为在替换之后T const
变为完全匹配char const[4]
.在第一种情况下,推断出的A
是再次比原来更合格的A
,取代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
}
Run Code Online (Sandbox Code Playgroud)
海湾合作委员会是正确的;在 VS 的模板参数列表const
中不应该存在:
\n\n\n\n\n
[C++11: 14.8.2/3]:
执行此替换后,将执行 8.3.5 中描述的功能参数类型调整。[ 示例:的参数类型\xe2\x80\x9cvoid ()(const int, int[5])\xe2\x80\x9d
变为\xe2\x80\x9cvoid(*)(int,int*)\xe2\x80\x9d
。\xe2\x80\x94end 示例] [ 注意:函数参数声明中的顶级限定符不会影响函数\n 类型,但仍会影响函数内函数参数变量的类型。\xe2\x80\x94end 注] [ 示例:Run Code Online (Sandbox Code Playgroud)\n\ntemplate <class T> void f(T t);\ntemplate <class X> void g(const X x);\ntemplate <class Z> void h(Z, Z*);\n\nint main() {\n // #1: function type is f(int), t is non const\n f<int>(1);\n\n // #2: function type is f(int), t is const\n f<const int>(1);\n\n // #3: function type is g(int), x is const\n g<int>(1);\n\n // #4: function type is g(int), x is const\n g<const int>(1);\n\n // #5: function type is h(int, const int*)\n h<const int>(1,0);\n}\n
\xe2\x80\x94结束示例]
\n
(示例 4 是相关的。)
\n\n\n\n\n\n
[C++11: 14.8.2/5]:
替换和调整后的函数类型用作模板参数推导的函数模板类型。 [..]
也可能相关:
\n\n\n\n从函数调用推导模板参数
\n\n
\n[C++11: 14.8.2.1/2]:
如果P
不是引用类型:\n
\n- 如果
\nA
是数组类型,则使用数组到指针标准转换(4.2)生成的指针类型来代替A
类型推导;否则,- 如果
\nA
是函数类型,则使用函数到指针标准转换(4.3)生成的指针类型来代替A
类型推导;否则,- 如果是 cv 限定类型,则类型推导将忽略 \xe2\x80\x99s 类型的
\nA
顶级cv 限定符A
归档时间: |
|
查看次数: |
277 次 |
最近记录: |