更多 C++ 慣用法/結交新朋友
外觀
簡化類模板友元函式的建立。
友元函式通常用於為類提供一些輔助的附加介面。例如,插入 (<<)、提取 (>>) 運算子和過載的算術運算子通常是友元。與為非模板類宣告友元函式相比,宣告類模板的友元函式要複雜一些。當涉及模板時,類及其友元之間存在四種關係。
- 一對多:非模板函式可以是所有模板類例項化的友元。
- 多對一:模板函式的所有例項化可以是普通非模板類的友元。
- 一對一:使用一組模板引數例項化的模板函式可以是使用相同模板引數例項化的一組模板類的友元。這也是普通非模板類和普通非模板友元函式之間的關係。
- 多對多:模板函式的所有例項化可以是模板類所有例項化的友元。
這裡我們感興趣的是一對一關係,因為它在 C++ 中需要額外的語法。下面是一個例子。
template<typename T>
class Foo {
T value;
public:
Foo(const T& t) { value = t; }
friend ostream& operator<<(ostream&, const Foo<T>&);
};
template<typename T>
ostream& operator<<(ostream& os, const Foo<T>& b) {
return os << b.value;
}
上面的例子對我們來說是不合適的,因為插入器不是模板,但它仍然使用模板引數 (T)。這是一個問題,因為它不是成員函式。operator<<( ) 必須是模板,以便為每個 T 建立不同的特化。
這裡的解決方案是在類宣告之前的友元宣告之前宣告一個插入運算子模板,並在友元宣告中新增 <>。它表示先前宣告的模板應該成為友元。
// Forward declarations
template<class T> class Foo;
template<class T> ostream& operator<<(ostream&,
const Foo<T>&);
template<class T>
class Foo {
T value;
public:
Foo(const T& t) { value = t; }
friend ostream& operator<< <>(ostream&, const Foo<T>&);
};
template<class T>
ostream& operator<<(ostream& os, const Foo<T>& b)
{
return os << b.value;
}
上述解決方案的缺點是它非常冗長。
Dan Saks 建議了另一種方法來克服上述解決方案的冗長性。他的解決方案被稱為“結交新朋友”慣用法。這個想法是在類模板內部定義友元函式,如下所示。
template<typename T>
class Foo {
T value;
public:
Foo(const T& t) { value = t; }
friend ostream& operator<<(ostream& os, const Foo<T>& b)
{
return os << b.value;
}
};
這樣的友元函式不是模板,但模板充當“製造”新朋友的工廠。為每個 Foo 的特化建立一個新的非模板函式。