使用这个示例程序,我观察到g和clang中的不同行为
foo.h中:
#include <iostream> namespace Bar { class Foo { public: Foo(int x) : _x(x) {} int x() const { return _x; } private: int _x; }; } std::ostream& operator <<(std::ostream& os,const Bar::Foo* foo);
Foo.cpp中
#include <Foo.h> using namespace std; ostream& operator <<(ostream& os,const Bar::Foo* foo) { return os << foo->x(); }
main.cpp中
#include <iostream> using namespace std; template<typename T> void print(const T& t) { cout << t << endl; } #include <Foo.h> int main(int argc,char** argv) { Bar::Foo* foo = new Bar::Foo(5); print(foo); }
用俚语和g编译产生不同的结果:
air:~ jose$clang++ Foo.cpp main.cpp -I. air:~ jose$./a.out 0x7ff9e84000e0 air:~ jose$g++ Foo.cpp main.cpp -I. air:~ jose$./a.out 5
哪一个是正确的,为什么?
解决方法
在这种特殊情况下,cl声是正确的.
问题是如何在模板打印中执行查找.在表达式中,打印对运算符<<是依赖的依赖名称的名称解析在14.6.4中处理:
In resolving dependent names,names from the following sources are considered:
— Declarations that are visible at the point of definition of the template.
— 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.
在您的情况下,您的运算符的声明在模板定义点不可见,因为标题之后包含,并且它不存在于函数参数的任何关联命名空间中(即:: std for: :std :: ostream and :: Bar for :: Bar :: Foo *),所以它不会被找到.
现在,:: std中有一个重载,它占用一个void *,这将由Argument Dependent Lookup找到. :: Bar :: Foo *将被转换为一个void *,并打印地址.
也就是说,在标准兼容的编译器中.
始终在同一个名称空间中定义适用于您的类型的运算符,该名称空间中包含它们应用的类型.让论据依赖查找对你来说是个魔术.它是专门为了这个特殊目的而设计的,使用它.