問題是編譯器不會嘗試使用您提供的模板operator<<
,而是使用非模板版本。
當你在一個類中聲明一個朋友時,你將在封閉範圍內注入該函數的聲明。下面的代碼具有聲明(而不是限定)的自由功能,通過恆定的基準取non_template_test
參數的效果:
class non_template_test
{
friend void f(non_template_test const &);
};
// declares here:
// void f(non_template_test const &);
同樣的情況與模板類,即使在這種情況下,它是一個小不太直觀。當你在模板類體中聲明(而不是定義)一個友元函數時,你正在用這個確切的參數聲明一個自由函數。請注意,您正在聲明一個函數,而不是模板函數:
template<typename T>
class template_test
{
friend void f(template_test<T> const & t);
};
// for each instantiating type T (int, double...) declares:
// void f(template_test<int> const &);
// void f(template_test<double> const &);
int main() {
template_test<int> t1;
template_test<double> t2;
}
這些自由函數是聲明但未定義的。這裏棘手的部分是這些免費函數不是模板,而是定義了常規的免費函數。當您添加模板函數混進去你:
template<typename T> class template_test {
friend void f(template_test<T> const &);
};
// when instantiated with int, implicitly declares:
// void f(template_test<int> const &);
template <typename T>
void f(template_test<T> const & x) {} // 1
int main() {
template_test<int> t1;
f(t1);
}
當編譯器擊中它實例化模板template_test
與int
類型的主要功能,並且聲明免費功能void f(template_test<int> const &)
是不模板。當它找到呼叫f(t1)
時,有兩個f
符號匹配:當template_test
已實例化並且已聲明和定義1
的模板版本時聲明(而未定義)的非模板f(template_test<int> const &)
。非模板版本優先,編譯器與之匹配。
當鏈接器嘗試解析f
的非模板版本時,它無法找到該符號,從而失敗。
我們該怎麼辦?有兩種不同的解決方案。在第一種情況下,我們讓編譯器爲每個實例化類型提供非模板化函數。在第二種情況下,我們將模板版本聲明爲朋友。他們略有不同,但在大多數情況下是相同的。
具有編譯器生成的非模板功能我們:
template <typename T>
class test
{
friend void f(test<T> const &) {}
};
// implicitly
這與需要創造儘可能多的非模板免費功能的效果。當編譯器在模板test
內找到好友聲明時,它不僅找到聲明,而且還找到實現,並將它們都添加到封閉範圍中。
製作模板化版本的朋友
爲了使模板是朋友,我們必須有它已經聲明,告訴編譯器我們想要的朋友實際上是一個模板,而不是一個非模板免費功能:
template <typename T> class test; // forward declare the template class
template <typename T> void f(test<T> const&); // forward declare the template
template <typename T>
class test {
friend void f<>(test<T> const&); // declare f<T>(test<T> const &) a friend
};
template <typename T>
void f(test<T> const &) {}
在這種情況下,之前宣佈f
,因爲我們必須轉發申報模板的模板。要聲明f
模板,我們必須首先轉發聲明test
模板。朋友聲明被修改爲包含尖括號,用於標識我們正在交友的元素實際上是模板而不是自由函數。
回到問題
再回到你的具體的例子,最簡單的解決方案是具有編譯器通過內聯友元函數聲明爲您生成功能:
template <typename T>
class BinaryTree {
friend std::ostream& operator<<(std::ostream& o, BinaryTree const & t) {
t.dump(o);
return o;
}
void dump(std::ostream& o) const;
};
隨着該代碼迫使編譯器爲每個實例化類型生成非模板化的operator<<
,並且該模板的dump
方法生成了函數委託。
saveToFile中引用了什麼類型的樹? – 2009-11-27 22:03:01
您是否在代碼中使用了命名空間(特別是當聲明'BinaryTree'和'operator <<'的重載)時?如果是這樣,請在上下文中顯示它們。 –
2009-11-27 22:03:03
請完整地發佈編譯器給出的錯誤消息的確切文本。 – 2009-11-27 22:46:53