12

为什么下面的代码在 g++ 中编译良好,但在 clang 中出错?

#include <iostream>

class Object {};

class Print
{
public:
    template <typename CharT>
    inline friend std::basic_ostream<CharT> & operator<<(std::basic_ostream<CharT> & out, const Object&)
    {
        return (out << "object");
    }
    static void f( const Object& str )
    {
        std::cout << str;
    }
};

int main()
{
    std::cout << Object() << std::endl;
    return 0;
}

证明链接:g++ / clang++

当我将朋友函数移动到全局命名空间时,为两个编译器(clang++ / g++)编译好的代码。

在这种情况下,哪种实现更兼容 C++ Standart?

4

2 回答 2

9

Clang在这里是正确的。在类中定义的友元函数只能通过参数依赖查找来找到,而不是通过普通查找。因为Print不是 的关联范围Object,所以operator<<不应找到 。部分引用标准:

7.3.1.2 命名空间成员定义[namespace.memdef]

3 在命名空间中首先声明的每个名称都是该命名空间的成员。如果非本地类中的友元声明首先声明了一个类、函数、类模板或函数模板97,则友元是最内层封闭命名空间的成员。友元声明本身不会使名称对非限定查找 (3.4.1) 或限定查找 (3.4.3) 可见。[ 注意:如果在命名空间范围内提供了匹配的声明(在授予友谊的类定义之前或之后),则朋友的名称将在其命名空间中可见。— 结束注释]如果调用友元函数或函数模板,则可以通过名称查找找到其名称,该名称查找考虑来自与函数参数类型关联的命名空间和类的函数(3.4.2)。

正如@sehe 提到的,添加operator<<to的正确方法Object是将其定义为全局函数(使用Object接口)或作为friend自身内部的函数(使用privatefrom 的函数Object),而不是在某些辅助类中。另请参阅 Herb Sutter 的旧 GotW 专栏“课堂上有什么?”

于 2015-01-17T20:07:53.997 回答
4

不确定的事情是在另一个类(不相关的)中声明一个静态(朋友)运算符。

  1. 你可以在周围的范围内创建它,有

    • 语义上没有区别
    • 没有理由让它成为朋友(因为Printer不以任何方式使用)
    • 如果你需要的话,你仍然可以让它成为朋友

    住在科利鲁

  2. 或者,使其他类相关

    有很多方法可以将命名空间(第 3.4.2 节)与函数查找类型相关联。例如,这个 hack 足以将Print类命名空间与Object类型相关联,因此 ADL 仍然可以工作:

    template <typename> struct Object_ {};
    typedef Object_<class Print> Object;
    

    也可以看到这个Live On Coliru

于 2015-01-17T20:08:44.017 回答