9

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

富.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();
}

主文件

#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

哪一个是正确的,为什么?

4

1 回答 1

13

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

问题是如何在模板中执行查找print。在表达式里面print调用isdependent从属名称的名称解析在 14.6.4 中处理:operator<<

在解析从属名称时,会考虑来自以下来源的名称:

— 在模板定义时可见的声明。

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

在您的情况下,您的运算符的声明在模板的定义点不可见,因为标头是在之后包含的,并且它不存在于函数参数的任何关联命名空间中(即::stdfor::std::ostream::Barfor ::Bar::Foo*),所以不会被发现。

现在,有一个::std需要 a的重载void*,并且将由 Argument Dependent Lookup 找到。将::Bar::Foo*转换为 avoid*并打印地址。

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

我忘记在此处添加此内容,仅将其留在评论中,但这很重要:

始终在包含它们所应用的类型的同一命名空间中定义适用于您的类型的运算符。让 Argument Dependent Lookup 为您带来神奇的效果。它是专门为服务于这个特定目的而设计的,使用它。

于 2013-04-12T16:14:31.330 回答