2016-03-05 44 views
8

考虑下面的代码片段:从属名称查找在函数模板:铿锵拒绝,GCC接受

struct X { }; 

namespace foo { 
    template <class T> 
    void bar() { T{} < T{}; } 

    void operator<(const X&, const X&) {} 
} 

int main() { 
    foo::bar<X>(); 
} 

铛拒绝这段代码,GCC接受它。这是一个海湾合作委员会的错误,或者这是一个铿锵虫?

+0

我不明白它是一个错误,因为'T'类型可以引用任何类型。如果那个类型'T'没有重载或支持'<'操作符,那么'bar'函数就会失败。既然你已经重载了'<'运算符,那就很好。我想这一切都归结为编译器如何读取代码,但语义 - 明智的是它不是容易出错的。 – Poriferous

+0

@Poriferous这没有任何意义。问题是关于'bar()'的正确行为是什么,对于没有'operator <'的类型'T'。正确的行为是:“是的,它发现'bar :: operator <'”(在这种情况下,clang有bug)或“代码格式不正确”(在这种情况下,gcc有bug)。 – Barry

+0

这是没有道理的,因为'bar'是一个函数,并且没有成员'operator <'。既然你已经为struct'X'定义了'operator <',我真的不知道这里有什么问题。即使运算符重载嵌入在结构体“X”中,代码​​仍应该正确编译。公平地说,它是铿锵有bug的,因为它似乎假定了什么类型。我的意思是,你是否尝试用'foo :: bar ()'来替换'foo :: bar ()',看看clang是否会拒绝该代码? – Poriferous

回答

5

海湾合作委员会是错误的铿锵是正确的。 GCC吞下无效代码的事实也在CLANG的兼容性页面here中提到。

以下列方式查找不合格的名称。

  1. 编译器在写入名称的范围内执行不合格的查找。 对于模板来说,这意味着查找是在模板定义的位置完成的,而不是在实例化的位置。由于operator<此时尚未声明,因此不合格的查找将无法找到它。
  2. 如果名称被称为函数,那么编译器也会进行参数相关查找(ADL)。 (有时,不合格的查找可以抑制ADL;更多信息参见[basic.lookup.argdep]第3段)。在ADL中,编译器查看调用的所有参数的类型。当它找到一个类的类型时,它在该类的名字空间中查找该名称;结果是它在这些名称空间中找到的所有声明,以及来自非限定查找的声明。但是,编译器在知道所有参数类型之前不会执行ADL。
6

我相信这是一个海湾合作委员会的bug,提交为70099。从[temp.dep.res]:

在解决依赖性的名称,从以下来源的名称被认为是:
(1.1) - 声明是在模板的定义点可见。
(1.2) - 来自实例化上下文(14.6.4.1)和定义上下文中与函数参数类型关联的名称空间的声明。

foo::operator<()不是在模板中定义的点可见的,并且是不相关的命名空间从功能参数(X的关联的命名空间只是一个全局命名空间::)。所以我认为海湾合作委员会是错误的发现foo::operator<和铿锵是拒绝代码是正确的。