日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > c/c++ >内容正文

c/c++

【C++】模板类的友元函数

發布時間:2024/3/13 c/c++ 27 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【C++】模板类的友元函数 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

模板類友元函數

模板類的友元函數

參考:https://blog.csdn.net/dreamer_lhs/article/details/53580088

區分:友元是否為函數模板

  • 非模板友元
  • 約束(bound) 模板友元,友元類型取決于模板類被實例化的類型,一個實例化模板函數
  • 非約束(unbound) 模板友元,友元函數模板類型不同于類模板類型,一個通用型函數模板

非模板友元

友元函數不是一個模板函數

template<typename T> class HasFriend {... public:friend void counts(); // 對于所有的HasFriend對象的**非模板友元**friend void report(HasFriend<T> &); // 給非模板友元綁定參數 };void counts() { ... }void report(HasFriend<int>& t) {// 顯示化模板類型,此時就是HasFriend<int>模板類的友元 }

friend void counts() 可能是 HasFriend<int>, HasFriend<double> … 的友元,也就是面向所有HasFriend 具體對象的友元

counts 訪問模板類對象的方式可能有:訪問全局對象;使用全局指針訪問非全局對象;自己內部創建對象;訪問靜態數據成員

friend void report(HasFriend<T>& t) 其本身并不是模板函數,而是使用了一個模板參數,意味著友元函數的定義必須要顯式化模板類型

[缺點]: 非模板友元若是想要綁定參數,涉及到某些模板類的具體化,就必須顯示化模板類型,這讓程序嚴重缺乏靈活性


約束模板友元

友元函數是一個模板函數的實例化,且實例化的類型與類模板類型相關

// **step1.** 先在類定義的前面聲明每個**函數模板** template<typename T> class HasFriend; template<typename T> void b_counts(); template<typename T> void b_report(T&); // 函數模板// **step2.** 類中將**模板函數**聲明為友元 template<typename T> class HasFriend {... public:friend void b_counts<T> ();friend void b_report<> (HasFriend<T>&); // <>指出這里是b_report函數模板顯式**實例化**聲明// friend void b_report<HasFriend<T> > (HasFriend<T>&) 也可以這樣// 等同于 template void b_report<HasFriend<T> > (HasFriend<T> &);// 將會使用b_report(TT&) 模板生成一個HasFriend<T> 類型的實例(函數定義) };// **step3.** 為友元提供模板定義 template<typename T> void b_counts() { ... } template<typename T> void b_report(HasFriend<T>& t) // 函數模板**具體化**,其內部可以與b_report(T &)的定義不同 {cout << t.data; // 這里會有智能提示 }/** * 也可以這樣寫,那么b_report<HasFriend<T> > (HasFriend<T>&)就是從這個函數模板實例化而來 * template<typename T> * void b_report(T& t) * { * cout << t.data * } */

friend void b_report<> (HasFriend<T>& t ) 聲明中的<>指出這是函數模板的實例化。<>可以為空,因為可以從函數參數推斷出函數模板的模板參數類型為HasFriend<T>

friend void b_counts<T> () 這里沒有參數,因此必須使用函數模板語法<T>來指明實例化。如:b_counts<int>() 是對應HasFriend<int> 模板類的友元,每種HasFriend<T>模板類都有其自己的友元函數 b_counts<T>()

[注]:

(1)這里需要顯式指出函數模板具體化,<>寫法是模板函數實例化的一種語法,表示使用模板生成一個類型的函數定義;

(2)聲明定義三步走(類前聲明、類內聲明、類外定義,類內聲明要顯式)

探討友元函數加<>的意義(涉及函數模板的顯示實例化、顯示具體化)

如:

1. 使用 HasFriend<int> 創建對象 2. 隱式實例化了 HasFriend<int>3. HasFriend<int> 類內的所有函數于是都被確定(使用int替換所有的T) 4. 因為友元函數是模板函數,在類內以顯式實例化聲明 friend void b_report<> (HasFriend<T> &) 模板類的模板類型確定后,該顯式實例化聲明的T也就確定 變為:friend void b_report<HasFriend<int> >(HasFriend<int> &) (一個實例化的模板函數) 7. 編譯器看到上述聲明后,將使用b_report()模板生成一個HasFriend<int> 類型的實例 也就是使用b_report()模板生成一個 HasFriend<int> 類型的函數定義

其實在類內聲明的 b_report<>(HasFriend<T>&) 只是函數模板 b_report(T& t) 的一個實例化,當給b_report 傳參的實參類型為HasFriend<int>時,它就實例化為了 b_report<HasFriend<int> >(HasFriend<int> &),由函數模板實例化的用法可知,在這里就為其生成了定義

如果不加<>對友元函數模板的顯式實例化說明,b_report(HasFriend<int>&)會找不到函數的定義,因為它不是一個模板函數,必須要去匹配b_report(HasFriend<int>&)這個具體函數的定義。因為外面的b_report(T& t)是一個函數模板,沒有實例化b_report(HasFriend<int>&) 也就是沒有它的具體定義。在匯編中可以看到,foo是沒有被實例化的。

那為什么b_report(T& t) 不可以根據實參來實例化出一個b_report(HasFriend<int>&) 呢?因為根據匹配規則優先級,會去匹配更加具體的一個函數,也就是b_report(HasFriend<int>&) ,并沒有匹配b_report(T& t) ,因為沒有找到前者的定義,就報了鏈接錯誤。

對友元函數加了<>,就在告訴編譯器,將b_report(T& t)實例化為HasFriend<T>類型的模板函數,但感覺還沒完全具體化,隨著模板類的實例化,類內的友元模板函數也隨之確定實例。總之,就讓鏈接器知道,友元函數b_report<>(HasFriend<T>&)是有定義存在的。


非約束模板友元

友元函數是一個模板函數

template<typename T> class HasFriend { public:template<typename C, typename D>friend void show(C &, D & );... private:T data; };template<typename C, typename D> void show(C& c, D& d) {cout << c.data << d.data; }

約束模板友元,是在類模板外面聲明友元模板。通過在類模板內部聲明友元模板,可以創建非約束模板友元,即每個函數模板的具體化都是每個類模板具體化的友元(所有類模板具體化的友元)。對于非約束模板友元,友元模板的類型參數和類模板的類型參數是不同的

如:

int main() {HasFriend<int> i(10);HasFriend<double> d(2.3);show(i, d); }

函數調用 show(i, d)與下面具體化匹配:

void show<HasFriend<int> , HasFriend<double> > (HasFriend<int>& c, HasFriend<double>& d)

這也說明了它是所有HasFriend模板類的友元

當然也可以這樣寫,看著更像是參數為HasFriend模板類的友元函數:

template<typename T> class HasFriend{...template<typename C, typename D>friend void show(HasFriend<C> &, HasFriend<D> & ); }template<typename C, typename D> void show(HasFriend<C>& c, HasFriend<D>& d ) {cout << c.data << d.data; // 這樣有智能提示 }

調用show(i, d) 時,類型推導的就變成了:

void show<int, double> (HasFriend<int>& c, HasFriend<double>& d)

主要影響的是show<C, D>模板函數的模板參數類型的推導。但是還是不要寫成這樣,因為這樣會使友元被約束住了(參數類型只能是HasFriend<T>的某個具體化,而不是通用的),比如:

// 將show 改為: template<typename C, typename D> show( HasFriend<C>& c, HasFriend<D>& d ) {cout << c.data;cout << d; }HasFriend<int> i(10); show(i, 10); // 錯誤,第二個參數錯誤

假如并不都需要直接訪問兩個模板類的成員,就沒必要寫成這樣。將show寫為 show(C& c, D& d) 的通用性更強,所以更能體現**”非約束“**一詞


💡 約束 的原因:函數模板顯式實例化語法<> 使一個函數模板實例化,實例化的模板函數的參數是一個模板類型,參數類型受到類模板的實例化類型影響。因此每生成一個具體的類,就會實例化一個與其對應的友元函數。
受模板類的類型影響→約束;不受模板類的類型影響→非約束


[總結]: 如果類模板的友元函數是函數模板實例化,就是約束模板友元;如果類模板的友元函數是模板函數,就是非約束模板友元。

[建議]: 關于模板友元——約束與非約束:

為了保持“約束”與”非約束“的風格統一性,如果一定會使用到模板類成員,就使用約束寫法;如果不一定會使用到模板類成員,就使用非約束寫法

// 約束模板友元寫法 // 類前聲明: template<typename T> class HasFriend; template<typename T> void foo(T &);// 類內聲明: friend void foo<> (HasFriend<T> &); // 對函數模板實例化// 類外定義: template<typename T> void foo(HasFriend<T>& t) // 函數模板的具體化,函數重載 {// 直接訪問模板類成員 t.data 還有智能提示,豈不美滋滋 } // 非約束模板友元寫法 // 類內聲明: template<typename C, typename D> friend void foo(C &, D &);// 類外定義: template<typename C, typename D> void foo(C& c, D& d) {// 用到c.data ,d是其他類型,不是 HasFriend的具體類 }

模板類友元函數總結

根本原理,就是函數模板的具體化:約束條件,就是在類內對函數模板實例化聲明;非約束條件,就是在類內聲明一個與類模板類型不同的函數模板

總結

以上是生活随笔為你收集整理的【C++】模板类的友元函数的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。