操作符重载clang++和g++不同的输出

Operator overloading clang++ and g++ different output

本文关键字:输出 g++ 重载 clang++ 操作符      更新时间:2023-10-16

对于这个示例程序,我观察到g++和clang中的不同行为

foo:

#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);
}

用clang++和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

在这种特殊情况下,clang++是正确的。

问题是如何在模板print中执行查找。在print内部的表达式中,对operator<<的调用依赖于相关名称的名称解析在14.6.4:

中处理。

在解析依赖名称时,考虑来自以下来源的名称:

-在模板定义点可见的声明。

-与实例化上下文(14.6.4.1)和定义上下文的函数实参类型相关联的命名空间的声明。

在你的例子中,你的操作符的声明在模板的定义点是不可见的,因为头文件是在之后包含的,它不存在于函数参数的任何相关的命名空间中(即::std::ostream::std::Bar::Foo*::Bar),所以它不会被发现。

现在,在::std中有一个重载,它接受一个void*,它将通过参数依赖查找找到。::Bar::Foo*将被转换为void*,地址将被打印。

也就是说,在符合标准的编译器中。

我忘记在这里添加了,只在评论中留下了它,但它足够重要:

始终将应用于类型的操作符定义在保存其应用的类型的同一命名空间中。让参数依赖查找为你实现它的魔力。