使用 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
推导了两个参数的不同类型。因此,代码格式错误。
使用 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
推导了两个参数的不同类型。因此,代码格式错误。