究竟什么是“破"?使用 Microsoft Visual C++ 的两阶段模板实例化?
阅读关于 SO 的问题、评论和答案,我一直听说 MSVC 没有正确实现两阶段模板查找/实例化.
Reading questions, comments and answers on SO, I hear all the time that MSVC doesn't implement two-phase template lookup / instantiation correctly.
据我目前所知,MSVC++ 只对模板类和函数进行基本的语法检查,并没有检查模板中使用的名称是否至少已声明或类似的内容.
From what I understand so far, MSVC++ is only doing a basic syntax check on template classes and functions and doesn't check that names used in the template have atleast been declared or something along those lines.
这是正确的吗?我错过了什么?
Is this correct? What am I missing?
推荐答案
我只是从我的 "笔记本"
int foo(void*);
template<typename T> struct S {
S() { int i = foo(0); }
// A standard-compliant compiler is supposed to
// resolve the 'foo(0)' call here (i.e. early) and
// bind it to 'foo(void*)'
};
void foo(int);
int main() {
S<int> s;
// VS2005 will resolve the 'foo(0)' call here (i.e.
// late, during instantiation of 'S::S()') and
// bind it to 'foo(int)', reporting an error in the
// initialization of 'i'
}
上面的代码应该在标准的 C++ 编译器中编译.但是MSVC(2005和2010 Express)会因为两阶段查找的实现不正确而报错.
The above code is supposed to compile in a standard C++ compiler. However, MSVC (2005 as well as 2010 Express) will report an error because of incorrect implementation of two-phase lookup.
如果你仔细观察,这个问题实际上是两层的.从表面上看,Microsoft 的编译器未能对非依赖表达式 foo(0)
执行早期(第一阶段)查找是一个明显的事实.但是它之后所做的并不能真正作为第二个查找阶段的正确实现.
And if you look closer, the issue is actually two-layered. At the surface, it is the obvious fact that Microsoft's compiler fails to perform early (first phase) lookup for a non-dependent expression foo(0)
. But what it does after that does not really behave as a proper implementation of the second lookup phase.
语言规范明确指出,在第二个查找阶段只有 ADL 指定的命名空间得到扩展,在定义点和实例化点之间积累了额外的声明.同时,非 ADL 查找(即普通的非限定名称查找)不会被第二阶段扩展 - 它仍然可以看到那些并且只看到那些在第一阶段可见的声明.
The language specification clearly states that during the second lookup phase only ADL-nominated namespaces get extended with additional declarations accumulated between the point of definition and point of instantiation. Meanwhile, non-ADL lookup (i.e. ordinary unqualified name lookup) is not extended by the second phase - it still sees those and only those declarations that were visible at the first phase.
这意味着在上面的例子中,编译器也不应该在第二阶段看到 void foo(int)
.换句话说,MSVC 的行为不能仅仅用MSVC 将所有查找推迟到第二阶段"来描述.MSVC 实现的也不是第二阶段的正确实现.
That means that in the above example the compiler is not supposed to see void foo(int)
at the second phase either. In other words, the MSVC's behavior cannot be described by a mere "MSVC postpones all lookup till the second phase". What MSVC implements is not a proper implementation of the second phase either.
为了更好地说明问题,请考虑以下示例
To better illustrate the issue, consider the following example
namespace N {
struct S {};
}
void bar(void *) {}
template <typename T> void foo(T *t) {
bar(t);
}
void bar(N::S *s) {}
int main() {
N::S s;
foo(&s);
}
请注意,即使模板定义中的 bar(t)
调用是在第二个查找阶段解析的 依赖 表达式,它仍应解析为 void栏(空*)
.在这种情况下,ADL 不会帮助编译器找到 void bar(N::S *s)
,而常规的非限定查找不应该被第二阶段扩展",因此不是也应该看到 void bar(N::S *s)
.
Note that even though bar(t)
call inside the template definition is a dependent expression resolved at the second lookup phase, it should still resolve to void bar(void *)
. In this case ADL does not help the compiler to find void bar(N::S *s)
, while the regular unqualified lookup is not supposed to get "extended" by the second phase and thus is not supposed to see void bar(N::S *s)
either.
然而,Microsoft 的编译器会解析对 void bar(N::S *s)
的调用.这是不正确的.
Yet, Microsoft's compiler resolves the call to void bar(N::S *s)
. This is incorrect.
这个问题在 VS2015 中依然存在.
The problem is still present in its original glory in VS2015.
相关文章