【问题标题】:Operator overloading clang++ and g++ different output运算符重载clang++和g++不同的输出
【发布时间】:2013-04-12 15:51:25
【问题描述】:

通过这个示例程序,我观察到 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);
}

用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

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

【问题讨论】:

  • 如果您将#includes 解析为单个可粘贴实体,对人们来说可能会更容易。
  • 我觉得有趣的唯一部分是它甚至可以工作。 operator&lt;&lt; 接受Bar::Foo const *,但通过print() 模板您发送Bar::Foo * const(此时引用无关紧要)。取出模板并将指针直接发送到cout,看看两个工具链都给你什么。 更好是一样的。
  • 无论哪个正确,您的代码都是错误(或至少明显可以改进)。您应该在命名空间 Bar 中声明/定义 operator&lt;&lt;,以便 Argument Dependent Lookup 找到它。
  • 如果我直接将指针发送到 cout 而不是使用“打印”两个输出 5
  • @DavidRodríguez-dribeas:虽然我同意你的观点,运营商应该是Bar 命名空间的一部分,但这不是问题的根本原因。调用发生在全局命名空间中,因此名称查找应该找到 OP 的重载,并且比接受 void const* 的更喜欢它。

标签: c++ name-lookup


【解决方案1】:

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

问题是如何在模板print 中执行查找。在print 内部的表达式中,对operator&lt;&lt; 的调用是依赖依赖名称的名称解析在 14.6.4 中处理:

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

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

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

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

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

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

这里忘记加了,只在评论里留下了,不过已经够重要了:

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

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-06-19
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多