为了账号安全,请及时绑定邮箱和手机立即绑定

使用Microsoft Visual C ++的两阶段模板实例化到底是什么“打破”?

使用Microsoft Visual C ++的两阶段模板实例化到底是什么“打破”?

C++
蛊毒传说 2019-07-27 10:15:09
使用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的原始荣耀中。


查看完整回答
反对 回复 2019-07-27
?
婷婷同学_

TA贡献1844条经验 获得超8个赞

从历史上看,gcc也没有正确实现两阶段名称查找。这显然很难达到,或者至少没有太大的激励......

  • gcc的4.7索赔正确地执行它,最后

  • CLang旨在实现它,禁止错误,它在ToT上完成并将进入3.0

我不知道为什么VC ++编写者从未选择正确实现这一点,在CLang上实现类似的行为(对于微软的兼容性)暗示在翻译单元末端延迟模板的实例化可能会有一些性能提升(并不意味着错误地实施查找,但使其更加困难)。此外,鉴于正确实施的明显困难,它可能更简单(并且更便宜)。

我会注意到VC ++首先是商业产品。它是满足客户需求的驱动力。


查看完整回答
反对 回复 2019-07-27
  • 3 回答
  • 0 关注
  • 439 浏览

添加回答

举报

0/150
提交
取消
意见反馈 帮助中心 APP下载
官方微信