C++ “究竟是什么?”;“破碎的”;使用Microsoft Visual C++';s两阶段模板实例化?

C++ “究竟是什么?”;“破碎的”;使用Microsoft Visual C++';s两阶段模板实例化?,c++,templates,visual-c++,instantiation,C++,Templates,Visual C++,Instantiation,通过阅读问题、评论和答案,我一直听说MSVC没有正确地实现两阶段模板查找/实例化 据我目前所知,MSVC++只对模板类和函数进行基本语法检查,而不检查模板中使用的名称是否至少已声明或类似的内容 这是正确的吗?我遗漏了什么?Clang项目有一个非常好的两阶段查找编写,以及各种实现差异: 短版本:两阶段查找是C++标准定义的在模板代码中查找名称的行为的名称。基本上,有些名称被定义为依赖(其规则有点混乱),在实例化模板时必须查找这些名称,在解析模板时必须查找独立名称。这既很难实现(显然),又让开发人员

通过阅读问题、评论和答案,我一直听说MSVC没有正确地实现两阶段模板查找/实例化

据我目前所知,MSVC++只对模板类和函数进行基本语法检查,而不检查模板中使用的名称是否至少已声明或类似的内容


这是正确的吗?我遗漏了什么?

Clang项目有一个非常好的两阶段查找编写,以及各种实现差异:


短版本:两阶段查找是C++标准定义的在模板代码中查找名称的行为的名称。基本上,有些名称被定义为依赖(其规则有点混乱),在实例化模板时必须查找这些名称,在解析模板时必须查找独立名称。这既很难实现(显然),又让开发人员感到困惑,因此编译器往往无法按照标准实现它。为了回答你的问题,它看起来像Visual C++延迟所有查找,但搜索模板上下文和实例化上下文,所以它接受了很多代码,标准说它不应该。我不确定它是否接受代码,或者更糟的是,它应该以不同的方式解释代码,但这似乎是可能的。

我将从我的

请注意,即使模板定义中的
bar(t)
调用是在第二个查找阶段解析的依赖表达式,它仍应解析为
void bar(void*)
。在这种情况下,ADL不能帮助编译器找到
无效条(N::S*S)
,而常规的非限定查找不应该在第二阶段得到“扩展”,因此也不应该看到
无效条(N::S*S)

然而,微软的编译器解决了对
voidbar(N::s*s)
的调用。这是不正确的


这个问题仍然存在于VS2015的最初辉煌中。

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

  • gcc 4.7最终要求
  • CLang的目标是实现它,揭露错误,它是在ToT上完成的,并将进入3.0
我不知道为什么VC++的作者从来没有选择正确地实现这一点,在CLang上实现类似的行为(对于microsoft Compability)暗示,在翻译单元的末尾延迟模板的实例化可能会有些困难(这并不意味着不正确地实现查找,但会使查找变得更加困难)。此外,考虑到正确实现的明显困难,它可能更简单(也更便宜)

我要指出的是,VC++首先是一种商业产品。它是由满足客户需求驱动的。

简短回答 使用/Za禁用语言扩展

更长的答案 我最近在调查这个问题,很惊讶在VS 2013下,标准[temp.dep]p3中的以下示例产生了错误的结果:

typedef double A;
template<class T> class B {
public:
    typedef int A;
};
template<class T> struct X : B<T> {
public:
    A a;
};

int main()
{
    X<int> x;
    std::cout << "type of a: " << typeid(x.a).name() << std::endl;
}
虽然它应该打印
double
。使VS符合标准的解决方案是禁用语言扩展(选项/Za),但现在x.a的类型将解析为double,并且使用基类的依赖名称的其他情况也将符合标准。我不确定这是否启用两阶段查找


[更新日期:2019年7月]vs 2015也是如此,但VS2019正确地显示了
double
。根据这篇文章,它是从vs 2017开始修复的。

现在MSVC已经实现了大部分两阶段名称查找,我希望这篇博文完全回答了这个问题:

我没有这方面的参考资料,但我相信现在已经实现了,很可能在VC 2005或2008中。@Simon:我不认为是这样。@ildjarn:我收回我的声明!模板编译已经有了显著的改进,但两阶段查找显然仍然过于松散。从定义来看,我认为break可能是一个过强的术语。我在VC2010中的经验给我的印象是编译器d在第一阶段查找名称的次数比要求的少很多。至少我发现gcc有更多错误,VC跳过了这些错误。你可能会发现最近提交给Visual Studio的错误很有趣:MSVC需要名称空间,即使它被提升了——也许,如果更多人投票,它会被更快地修复。它会以不同的方式解释它——例如,如果你eclare better match重载在模板声明后,标准定义了较差的匹配,但MSVC将选择较好的匹配。在VC2010 SP1中确认了相同的行为。我不希望MS现在更改此行为,因为相对较小的可移植性损失带来了太多的兼容性负担。实际上,我建议标准改变了,使这样的代码(意图不明确)需要诊断。但这是另一个拉比特洞…@Simon:当发现编译器不符合标准时,添加一个向后兼容的命令行选项并默认未来的标准符合性是合理的。这不是MS或任何编译器编写者可以接受的地方“相对较小的可移植性损失”有助于将他们的客户锁定在……这很容易被故意滥用,而且历史表明,他们经常被冷嘲热讽地接受。否则,在这里,没有什么比在名称查找的100个其他部分中更模糊的了——这是荒谬的“推荐”“对此标准的更改。@Simon:我只能不同意……在Andrey的示例中,模板编写器对foo()的作用有一个概念,并希望在此基础上调用它。稍后替换其他foo()会使客户端代码影响模板实例化,这有两个严重问题:1)对象可能有不同的实例化
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);
}
typedef double A;
template<class T> class B {
public:
    typedef int A;
};
template<class T> struct X : B<T> {
public:
    A a;
};

int main()
{
    X<int> x;
    std::cout << "type of a: " << typeid(x.a).name() << std::endl;
}
type of a: int