使用Microsoft Visual C ++的两阶段模板实例化到底是什么“打破”?在SO上阅读问题,评论和答案,我一直听说MSVC没有正确地实现两阶段模板查找/实例化。据我所知,到目前为止,MSVC ++只对模板类和函数进行了基本的语法检查,并没有检查模板中使用的名称是否至少被声明了或者沿着这些行。它是否正确?我错过了什么?
3 回答
慕姐4208626
TA贡献1852条经验 获得超7个赞
我只是从我的“笔记本”中复制一个例子
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)将报告错误,因为两阶段查找的实现不正确。
如果你仔细观察,问题实际上是两层的。从表面上看,显而易见的事实是,Microsoft的编译器无法对非依赖表达式执行早期(第一阶段)查找foo(0)
。但它之后的作用并不像第二个查找阶段的正确实现那样。
语言规范明确指出,在第二个查找阶段,只有在定义点和实例化点之间累积的附加声明才会扩展ADL指定的命名空间。同时,非ADL查找(即普通的非限定名称查找)不会被第二阶段扩展 - 它仍然可以看到那些以及仅在第一阶段可见的那些声明。
这意味着在上面的例子中,编译器也不应该void foo(int)
在第二阶段看到。换句话说,仅仅“MSVC将所有查找推迟到第二阶段”不能描述MSVC的行为。MSVC实现的不是第二阶段的正确实现。
为了更好地说明问题,请考虑以下示例
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 bar(void *)
。在这种情况下,ADL无法帮助编译器查找void bar(N::S *s)
,而常规的非限定查找不应该被第二阶段“扩展”,因此也不应该看到void bar(N::S *s)
。
然而,微软的编译器解决了对它的调用void bar(N::S *s)
。这是不正确的。
这个问题仍然存在于VS2015的原始荣耀中。
- 3 回答
- 0 关注
- 467 浏览
添加回答
举报
0/150
提交
取消