clang/g++ 与友元函数的区别

clang/g++ difference with friend function

为什么下面的代码在 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++ 标准?

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

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

3 Every name first declared in a namespace is a member of that namespace. If a friend declaration in a non-local class first declares a class, function, class template or function template97 the friend is a member of the innermost enclosing namespace. The friend declaration does not by itself make the name visible to unqualified lookup (3.4.1) or qualified lookup (3.4.3). [ Note: The name of the friend will be visible in its namespace if a matching declaration is provided at namespace scope (either before or after the class definition granting friendship). — end note ] If a friend function or function template is called, its name may be found by the name lookup that considers functions from namespaces and classes associated with the types of the function arguments (3.4.2).

正如@sehe 提到的,将 operator<< 添加到 Object 的正确方法是将其定义为全局函数(使用 Object 接口)或 friend 函数本身(使用 Object 中的 private 函数),而不是在某些辅助 class 中。另请参阅 Herb Sutter 的旧 GotW 专栏 "What's in a class?"

有问题的是在 another class 中声明一个静态(友元)运算符(这是不相关的)。

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

    • 语义上没有区别
    • 没有理由把它当朋友(因为 Printer 没有任何用处)
    • 如果需要,您仍然可以将其加为好友

    Live On Coliru

  2. 或者,使另一个class相关

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

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

    也看到这个Live On Coliru