使用 friend 来减少冗长

Using friend to reduce verbosity

使用 friend 在 class 定义中定义全局函数被认为是一种好的做法,即使不需要访问私有成员也是如此。例如

template<typename T>
class A {
public:
    A(T v);
    T value() const;

    friend A operator+(T n, const A& a) {
        return A(a.value() + n);
    }
};

而不是

template<typename T>
class A {
public:
    A(T v);
    T value() const;
};

template<typename T>
A<T> operator+(T n, const A<T>& a) {
    return A<T>(a.value() + n);
}

尽管 operator+ 仅使用 value(),即 public。 这种做法常见吗,我们不推荐吗?

这里 friend 有一个主要优势。当我们定义:

friend A operator+(T, const A&);

不是函数模板。这只是一个函数——一个只有 ADL 才能找到的特殊函数。但由于它不是函数模板,转换仍然可以发生。另一方面:

template <class T>
A<T> operator+(T, const A<T>&)

是一个普通的旧函数模板,具有关于模板类型推导的所有普通规则。

为什么这很重要?考虑:

A<double> a(4.2);
5 + a;

在第一种情况下,这完全没问题。我们找到 operator+(double, const A<double>&)5 被转换为 5.0,这是一个允许的转换,我们得到 A<double>(9.2).

在第二种情况下,模板推导失败,因为 T 推导了两个参数的不同类型。因此,代码格式错误。