Dependent name lookup in function template: clang rejects, gcc accepts
考虑下面的片段:
1 2 3 4 5 6 7 8 9 10 11 12
| struct X { };
namespace foo {
template <class T>
void bar() { T{} < T{}; }
void operator<(const X&, const X&) {}
}
int main() {
foo::bar<X>();
} |
这clang rejects GCC代码,接受它。这是一个编译器错误或是错误的,这是一clang吗?
- 我看不出这是什么bug,因为T类型可以引用任何类型。如果该类型的T不过载或不支持<运算符,那么bar函数将失败。因为你已经超载了<的操作员,一切都很好。我想这都取决于编译器是如何读取代码的,但从语义上讲,它不容易出错。
- @多孔的,没有任何意义。问题是,对于没有operator<的T类型,bar()的正确行为是什么。正确的行为是:"是的,它发现bar::operator<"(在这种情况下,clang有错误)或"代码格式错误"(在这种情况下,gcc有错误)。
- 这毫无意义,因为bar是一个函数,没有成员operator<。因为您已经为结构X定义了operator <,所以我真的不知道这里有什么问题。即使运算符重载嵌入到结构X中,代码仍应正确编译。公平地说,有缺陷的是clang,因为它似乎假定了一个类型是什么。我的意思是,你有没有试过用类似于foo::bar()的东西替换foo::bar(),看看clang是否也拒绝了这个代码?
我相信这是一个GCC的bug,归档号为70099。来自[温度下降值]:
In resolving dependent names, names from the following sources are considered:
(1.1) — Declarations that are visible at the point of definition of the template.
(1.2) — Declarations from namespaces associated with the types of the function arguments both from the instantiation context (14.6.4.1) and from the definition context.
号
在模板的定义点,foo::operator<()不可见,也不在函数参数的关联命名空间中(X的关联命名空间只是全局命名空间::而已)。所以我认为GCC发现foo::operator<是错误的,clang拒绝代码是正确的。
GCC是错误的,clang是正确的。在Clang的兼容性页面中也提到了GCC吞下无效代码的事实。
不合格名称按以下方式查找。
编译器在编写名称的范围内执行非限定查找。对于模板,这意味着查找是在定义模板的位置完成的,而不是在实例化模板的位置。由于此时operator<还没有被声明,不合格查找就找不到。
如果名称像函数一样被调用,那么编译器也会执行与参数相关的查找(ADL)。(有时非限定查找可以抑制ADL;有关详细信息,请参阅[basic.lookup.argdep]第3段。)在ADL中,编译器查看调用的所有参数的类型。当它找到一个类类型时,它会在该类的命名空间中查找该名称;结果是它在这些命名空间中找到的所有声明,加上非限定查找中的声明。但是,编译器在知道所有参数类型之前不会执行ADL。