Clang 和 g++ 对运算符重载的处理方式不同?

Clang and g++ treat operator overload differently?

我正在使用 C++14(-std=c++1y 在 g++ 4.9.1 和 clang 3.5 上)。

首先,这是附件 A(存在 Foo 命名空间的地方):

#include <iostream>
#include <sstream>

namespace Foo
{
    struct A
    {};
}

void operator<<(std::ostream &os, Foo::A const &a)
{}

int main()
{
    Foo::A a;

    std::ostringstream() << a;

    return 0;
}

Clang 和 g++ 都对此感到厌恶,尽管出于不同的原因。


图表 B(没有 Foo 命名空间):

#include <iostream>
#include <sstream>

struct A
{};

void operator<<(std::ostream &os, A const &a)
{}

int main()
{
    A a;

    std::ostringstream() << a;

    return 0;
}

g++ 仍然 barfs,但 Clang 成功编译。

这样的期望合理吗?这是怎么回事?

您不能将临时对象绑定到非常量引用。在这种情况下 std::ostringstream() 创建一个临时对象并尝试绑定到 operator<<

的非常量引用参数

首先,标准为右值输出流 ([ostream.rvalue])

提供了一个包罗万象的 operator<<
template <class charT, class traits, class T>
basic_ostream<charT, traits>&
operator<<(basic_ostream<charT, traits>&& os, const T& x);

Effects: os << x

Returns: os

(右值输入流也有匹配的 operator>> - 参见 [istream.rvalue]。)

这是被调用的operator<<

其次,与模板一样,在这个函数模板的主体中,os << xoperator<< 的非限定查找是在模板定义上下文中完成的,它没有您的operator<< 可用。相反,您的重载必须由 ADL 找到,这反过来意味着它必须位于与 A.

相同的命名空间中

你的第二个版本应该可以编译,并且在这两种情况下,编译器确实发现你的重载很好。问题在于 libstdc++ 的实现(归结为 return os << x;)是不符合规范的,因为它假定 os << x 必须 return os。没有这个要求。

编辑:libstdc++ 错误报告是here;它已在主干中得到修复,并且已将修复程序反向移植到 4.8 和 4.9 分支。