然而,使用缩写形式更简单(而且更常用)。
现在返回到连接问题。混合模式代码编译,因为编译器知道我们想要调用一个特定的函数(取得一个 Rational<int> 和一个 Rational<int> 的 operator*),但是那个函数只是在 Rational 内部声明,而没有在此处定义。我们的意图是让 class 之外的 operator* template(模板)提供这个定义,但是这种方法不能工作。如果我们自己声明一个函数(这就是我们在 Rational template(模板)内部所做的事),我们就有责任定义这个函数。当前情况是,我们没有提供定义,这也就是连接器为什么不能找到它。
让它能工作的最简单的方法或许就是将 operator* 的本体合并到它的 declaration(定义)中:
template<typename T> class Rational { public: ...
friend const Rational operator*(const Rational& lhs, const Rational& rhs) { return Rational(lhs.numerator() * rhs.numerator(), // same impl lhs.denominator() * rhs.denominator()); // as in } //《C++箴言:声明为非成员函数的时机》 }; |
确实,这样就可以符合预期地工作:对 operator* 的混合模式调用现在可以编译,连接,并运行。万岁!
关于此技术的一个有趣的观察结论是 friendship 的使用对于访问 class 的 non-public parts(非公有构件)的需求并没有起到什么作用。为了让所有 arguments(实参)的 type conversions(类型转换)成为可能,我们需要一个 non-member function(非成员函数)(《C++箴言:声明为非成员函数的时机》 依然适用);而为了能自动实例化出适当的函数,我们需要在 class 内部声明这个函数。在一个 class 内部声明一个 non-member function(非成员函数)的唯一方法就是把它做成一个 friend(友元)。那么这就是我们做的。反传统吗?是的。有效吗?毫无疑问。
就像《C++箴言:理解inline化的介入和排除》阐述的,定义在一个 class 内部的函数被隐式地声明为 inline(内联),而这也包括像 operator* 这样的 friend functions(友元函数)。你可以让 operator* 不做什么事情,只是调用一个定义在这个 class 之外的 helper function(辅助函数),从而让这样的 inline declarations(内联声明)的影响最小化。在本文的这个示例中,没有特别指出这样做,因为 operator* 已经可以实现为一个 one-line function(单行函数),但是对于更复杂的函数体,这样做也许是合适的。"have the friend call a helper"(“让友元调用辅助函数”)的方法还是值得注意一下的。
Rational 是一个 template(模板)的事实意味着那个 helper function(辅助函数)通常也是一个 template(模板),所以典型情况下在头文件中定义 Rational 的代码看起来大致如下:
template<typename T> class Rational; // declare // Rational // template template<typename T> // declare const Rational<T> doMultiply(const Rational<T>& lhs, // helper const Rational<T>& rhs); // template template<typename T> class Rational { public: ...
friend const Rational<T> operator*(const Rational<T>& lhs, const Rational<T>& rhs) // Have friend { return doMultiply(lhs, rhs); } // call helper ... }; |
多数编译器基本上会强迫你把所有的 template definitions(模板定义)都放在头文件中,所以你可能同样需要在你的头文件中定义 doMultiply。(就像 Item 30 阐述的,这样的 templates(模板)不需要 inline(内联)。)可能看起来就像这样:
template<typename T> // define const Rational<T> doMultiply(const Rational<T>& lhs, // helper const Rational<T>& rhs) // template in { // header file, return Rational<T>(lhs.numerator() * rhs.numerator(), // if necessary lhs.denominator() * rhs.denominator()); } |
当然,作为一个 template(模板),doMultiply 不支持混合模式乘法,但是它不需要。它只被 operator* 调用,而 operator* 支持混合模式运算!本质上,函数 operator* 支持为了确保被相乘的是两个 Rational objects 而必需的各种 type conversions(类型转换),然后它将这两个 objects 传递给一个 doMultiply template(模板)的适当的实例化来做实际的乘法。配合行动,不是吗?
Things to Remember
·在写一个提供了 class template(类模板),而这个 class template(类模板)提供了一个函数,这个函数指涉到支持所有 parameters(参数)的 implicit type conversions(隐式类型转换)的 template(模板)的时候,把这些函数定义为 class template(类模板)内部的 friends(友元)。  
2/2 首页 上一页 1 2 |