模板偏特化翻譯 為什么基類的成員在父類中調(diào)用必須指明作用域或者使用this->?
日期:2023-03-11 12:39:36 / 人氣: 435 / 發(fā)布者:成都翻譯公司
在obj類模板調(diào)用含有模板參數(shù)的類成員函數(shù)之前,其實(shí)就是在”成員訪問運(yùn)算符(.我們看到,這個(gè)程序中Father類對(duì)象Obj1的成員函數(shù)Foo并沒有調(diào)用從Base類中繼承的exit函數(shù),我們應(yīng)該這樣改正這個(gè)程序(明確exit函數(shù)的歸屬位置,即我們到底要調(diào)用那里的exit函數(shù))。當(dāng)模板實(shí)例化時(shí),模板的全特化,模板的偏特化都適用時(shí)的調(diào)用順序:C++ 模板的技術(shù)基礎(chǔ)
內(nèi)容
關(guān)鍵字 typename 的使用
#include???
using?namespace?std;??
#include???
??
template???
void?PrintElement(T?const&?obj)??
{??
????typename?T::const_iterator?cpos;??
????typename?T::const_iterator?end?=?obj.cend();??
????for?(cpos?=?obj.cbegin();?cpos?!=?end;?cpos++)??
????{??
????????cout?<*cpos?<"?";??
????}??
????cout?<?Obj{?1,2,3,4,5,6,7,8,9,10?};??
????PrintElement(Obj);??
}??
有時(shí)T是一個(gè)類,其他類嵌套在該類中。例如,在STL標(biāo)準(zhǔn)模板庫中的vector類中,就有“const_iterator只讀迭代器”、“迭代器可讀可寫迭代器”等常用的嵌套類類型。.
當(dāng)我們構(gòu)建模板時(shí),我們會(huì)發(fā)現(xiàn)編譯器不會(huì)給我們?nèi)魏翁崾拘畔?。這是因?yàn)樵趯?shí)例化模板之前,編譯器不知道參數(shù) T 是什么類型。你在編譯器不知道時(shí)使用 T : :const_iterator,其他編譯器不知道你是什么。您可以使用 :: 訪問很多東西。類類型中的靜態(tài)靜態(tài)成員變量也可以通過類類型 T 加 :: 作用域訪問器來訪問。所以我們要讓編譯器知道 T::const_iterator 是 T 類型的嵌套子類類型模板偏特化翻譯,我們必須用 typename 聲明它。
例如:(解釋typename關(guān)鍵字的作用)
在上面的程序中,因?yàn)橛衪ypename,編譯器認(rèn)為ptr是一個(gè)T::SubType類型的指針,但是如果沒有typename呢?這時(shí)候編譯器會(huì)把T::SubType當(dāng)作T類型的靜態(tài)成員變量,那么T::SubType * ptr被認(rèn)為是兩個(gè)變量的相乘(編譯器認(rèn)為*是乘號(hào)?),如果你編譯如果你這樣操作設(shè)備,你的程序就會(huì)報(bào)錯(cuò),然后就涼了。
.模板構(gòu)造
#include???
#include???
using?namespace?std;??
??
template???
void?PrintBitset(bitset?const&?obj)??
{??
????cout?<,?allocator?>()?<?Obj;??
????PrintBitset(Obj);??
}?
???????
看上面的例子,我們會(huì)覺得有些不對(duì)勁,為什么要加一個(gè)“.template”呢?我們先來分析一下這個(gè)例子的特點(diǎn):
① obj 是一個(gè)包含模板參數(shù)的類對(duì)象;
② to_string 是包含模板參數(shù)的類成員函數(shù);
在這種情況下,為了防止編譯器:
把它想象成“兩個(gè)成員正在比較大小”:
我們這樣做:
#include???
#include???
using?namespace?std;??
??
template???
void?PrintBitset(bitset?const&?obj)??
{??
????cout?<,?allocator?>()?<?Obj;??
????PrintBitset(Obj);??
}??
obj 類模板在調(diào)用包含模板參數(shù)的類成員函數(shù)之前,實(shí)際上是在“成員訪問運(yùn)算符(.)”之后插入了模板關(guān)鍵字。其實(shí)在VS2017中試過很多次模板偏特化翻譯,感覺.template關(guān)鍵字加不加。沒有區(qū)別。我覺得《C++模板》一書中提到的這部分注意事項(xiàng)應(yīng)該是針對(duì)某些編譯器的。不同的編譯器的性能通常略有不同,可能會(huì)出現(xiàn)在這里。錯(cuò)誤,我覺得*好加上template關(guān)鍵字,這樣可以保證你的程序在任何編譯器中編譯都不會(huì)出錯(cuò)。
實(shí)踐小項(xiàng)目
要求:我們有N個(gè)人的團(tuán)隊(duì),每個(gè)團(tuán)隊(duì)的每個(gè)工人都有“姓名,每周工作時(shí)間(h)和每周工資(100元)”,我們要問這個(gè)團(tuán)隊(duì)的平均小時(shí)平均工資工資*高的人以及相應(yīng)工作人員的姓名。
代碼示例:
團(tuán)隊(duì).hpp
#include???
#include???
#include???
using?namespace?std;??
??
template???
class?Team;??
??
class?Worker??
{??
????template???
????friend?class?Team;??
private:??
????string?name;??
????int?NowSalary;??
????int?WorkTime;??
public:??
????Worker()?{};??
????Worker(const?Worker&?Obj)??
????{??
????????this->name?=?Obj.name;??
????????this->NowSalary?=?Obj.NowSalary;??
????????this->WorkTime?=?Obj.WorkTime;??
????}??
????Worker(string?name,?int?NowSalary,?int?WorkTime)??
????{??
????????this->name?=?name;??
????????this->NowSalary?=?NowSalary;??
????????this->WorkTime?=?WorkTime;??
????}??
????Worker&?operator?=?(const?Worker&?Obj)??
????{??
????????this->name?=?Obj.name;??
????????this->NowSalary?=?Obj.NowSalary;??
????????this->WorkTime?=?Obj.WorkTime;??
????????return?*this;??
????}??
????Worker&?operator?=?(const?Worker&&?Obj)??
????{??
????????this->name?=?Obj.name;??
????????this->NowSalary?=?Obj.NowSalary;??
????????this->WorkTime?=?Obj.WorkTime;??
????????return?*this;??
????}??
????bool?operator?>?(const?Worker&?obj)?const??
????{??
????????return?this->NowSalary?/?this->WorkTime?>?obj.NowSalary?/?obj.WorkTime;??
????}??
????bool?operator?(const?Worker&?obj)?const??
????{??
????????return?this->NowSalary?/?this->WorkTime???
class?Team??
{??
private:??
????Worker*?workers;??
????int?Pos;??
????int?MaxSalaryPerHour;??
????string?name;??
public:??
????Team()??
????{??
????????workers?=?new?Worker[N];??
????????Pos?=?0;??
????????MaxSalaryPerHour?=?0;??
????????name?=?"無";??
????}??
????void?Push(const?Worker&?Obj)??
????{??
????????if?(Pos?>=?N)??
????????{??
????????????throw?out_of_range("Over?Max?Range!");??
????????}??
????????workers[Pos]?=?Obj;??
????????Pos++;??
????}??
????void?Pop()??
????{??
????????if?(Pos?==?0)??
????????{??
????????????throw?out_of_range("Empty!");??
????????}??
????????Worker*?NewWorkers?=?new?Worker[--Pos];??
????????for?(int?i?=?0;?i???
????void?SearchMax()??
????{??
????????Worker?TheWorker;??
????????for?(int?i?=?0;?i??workers[i?+?1]???workers[i]?:?workers[i?+?1];??
????????}??
????????this->MaxSalaryPerHour?=?TheWorker.NowSalary?/?TheWorker.WorkTime;??
????????this->name?=?TheWorker.name;??
????????cout?<"Max?Salary?Per?Hour:"?<MaxSalaryPerHour?<",The?Worker's?Name:"?<name?<
主程序
#include???
using?namespace?std;??
#include?"Worker.hpp"??
#include???
??
Worker?Max(const?Worker&?var1,?const?Worker&?var2)??
{??
????return?var1?>?var2???var1?:?var2;??
}??
??
int?main()??
{??
????Team<4>?Obj1;??
????try??
????{??
????????Obj1.Push(Worker("張三",?19,?10));??
????????Obj1.Push(Worker("李四",?17,?12));??
????????Obj1.Push(Worker("王五",?10,?2));??
????????Obj1.Push(Worker("趙六",?19,?14));??
??
????????Obj1.template?SearchMax();??
????}??
????catch?(const?out_of_range&?exp)??
????{??
????????cout?<
為什么必須在父類中調(diào)用基類的成員來指定作用域或者使用this->?
代碼示例:
#include???
using?namespace?std;??
??
template???
class?Base??
{??
public:??
????void?exit()???
????{??
????????cout?<??
class?Father:?Base??
{??
public:??
????void?Foo()??
????{??
????????exit();??
????}??
};??
??
int?main()??
{??
????Father?Obj1;??
????Obj1.Foo();??
}??
操作結(jié)果:
我們看到這個(gè)程序中Father類對(duì)象Obj1的成員函數(shù)Foo并沒有調(diào)用繼承自Base類的exit函數(shù)。我們應(yīng)該像這樣更正這個(gè)程序(明確exit函數(shù)屬于哪里,也就是我們要調(diào)用exit函數(shù)的地方)。
代碼示例:
#include???
using?namespace?std;??
??
template???
class?Base??
{??
public:??
????void?exit()???
????{??
????????cout?<??
class?Father:?Base??
{??
public:??
????void?Foo()??
????{??
????????this->exit();?//?Base::exit()??
????}??
};??
??
int?main()??
{??
????Father?Obj1;??
????Obj1.Foo();??
}?
???????
與之前的程序相比,我們?cè)谕顺龊瘮?shù)中添加了 this->pointer/Base::scope 運(yùn)算符。使用其中之一(this->pointer 或 Base::scope 運(yùn)算符),編譯器可以確保退出函數(shù)不是從外部傳入,而是父類本身(this->pointer)/從基類繼承基礎(chǔ)(基礎(chǔ)::范圍運(yùn)算符)。
操作結(jié)果:
在《C++模板》一書中,有一段話講述了我上面所說的:
會(huì)員模板的優(yōu)點(diǎn)
我們有時(shí)會(huì)因?yàn)椤安煌愋偷淖远x Stacks 之間”無法相互賦值而感到惱火。為什么Stack類型對(duì)象不能調(diào)用Stack模板類中的overload=assignment運(yùn)算符給Stack類型對(duì)象賦值?
其實(shí)我們重載的=賦值運(yùn)算符是在模板類實(shí)例化(Stack)的時(shí)候使用的,確定了可以賦值的兩個(gè)對(duì)象的類類型:
主程序
#include?"Stack.hpp"??
#include???
using?namespace?std;??
??
int?main()??
{??
????Stack?Stack_Obj1(90);??
????Stack?Stack_Obj2;??
????Stack_Obj2?=?Stack_Obj1;??
}??
堆棧文件
#include???
using?namespace?std;??
#include???
??
template??>??
class?Stack??
{??
private:??
????CONT?element;??
public:??
????Stack()?=?default;??
????Stack(const?T&?obj);??
????Stack(const?Stack&?obj);??
??
????Stack&?operator?=?(const?Stack&?obj);??
????T&?operator?[]?(const?int&?order);??
};??
??
template??*/>??
T&?Stack::operator[](const?int&?order)??
{??
????return?this->element.at(order);??
}??
??
template??*/>??
Stack&?Stack::operator=(const?Stack&?obj)??
{??
????this->element.clear();??
????this->element.assign(obj.element.begin(),?obj.element.end());??
}??
??
template??*/>??
Stack::Stack(const?Stack&?obj)??
{??
????this->element.clear();??
????this->element.assign(obj.begin(),?obj.end());??
}??
??
template??*/>??
Stack::Stack(const?T&?obj)??
{??
????this->element.clear();??
????this->element.push_front(obj);??
}??
操作結(jié)果:
為什么會(huì)發(fā)生這種情況,請(qǐng)讓我詳細(xì)說明:
① 首先,我們使用 Stack 獲取實(shí)例化的模板。每個(gè)模板的賦值運(yùn)算符兩端的操作變量必須是變量類型;
②我們用“Stack_Obj1 = Stack_Obj2”來說明=賦值運(yùn)算符兩端的操作變量必須與Stack_Obj1變量的類型相同,即Stack類型;
③我們右邊的操作變量Stack_Obj2的操作類型是Stack類型的。編譯器一下子就糊涂了。我沒有=賦值運(yùn)算符可以同時(shí)操作兩種不同類型的變量相互賦值?那我就報(bào)錯(cuò)!
如何改進(jìn)它?這時(shí),成員模板的優(yōu)點(diǎn)就出來了:成員模板的主要優(yōu)點(diǎn)是“可以自由定義重載運(yùn)算符的操作對(duì)象,使一個(gè)運(yùn)算符可以同時(shí)操作兩種不同類型的變量。 ”
改進(jìn)的代碼(Stack.hpp):
#include???
using?namespace?std;??
#include???
??
template??>??
class?Stack??
{??
private:??
????CONT?element;??
public:??
????Stack()?=?default;??
????Stack(const?T&?obj);??
????Stack(const?Stack&?obj);??
??
????template???
????Stack&?operator?=?(const?Stack&?obj);??
??
????T&?operator?[]?(const?int&?order);??
????T&?Top();??
????void?Push(const?T&?obj);??
????void?Pop();??
????bool?empty();??
};??
??
template??*/>??
bool?Stack::empty()??
{??
????return?this->element.empty();??
}??
??
template??*/>??
void?Stack::Pop()??
{??
????if?(this->element.empty())??
????{??
????????throw?out_of_range("Empty!");??
????}??
????this->element.pop_front();??
}??
??
template??*/>??
void?Stack::Push(const?T&?obj)??
{??
????this->element.push_front(obj);??
}??
??
template??*/>??
T&?Stack::Top()??
{??
????if?(this->element.empty())??
????{??
????????throw?out_of_range("Empty!");??
????}??
????return?*(this->element.end()?-?1);??
}??
??
template???
template???
Stack&?Stack::operator=(const?Stack&?obj)??
{??
????if?((void*)this?!=?(void*)&obj)??
????{??
????????return?*this;??
????}??
??
????Stack?TempStack(obj);??
????this->element.clear();??
????while?(!TempStack.empty())??
????{??
????????this->element.push_front(TempStack.Top());??
????????TempStack.Pop();??
????}??
}??
??
template??*/>??
T&?Stack::operator[](const?int&?order)??
{??
????return?this->element.at(order);??
}??
??
??
template??*/>??
Stack::Stack(const?Stack&?obj)??
{??
????Stack?TempStack(obj);??
????this->element.clear();??
????while?(!TempStack.empty())??
????{??
????????this->element.push_front(TempStack.Top());??
????????TempStack.Pop();??
????}??
}??
??
template??*/>??
Stack::Stack(const?T&?obj)??
{??
????this->element.clear();??
????this->element.push_front(obj);??
}??
你可能會(huì)糊涂,沒關(guān)系,下面是Stack類的成員函數(shù)列表:
class?Stack??
{??
private:??
????CONT?element;??
public:??
????Stack()?=?default;??
????Stack(const?T&?obj);??
????Stack(const?Stack&?obj);??
??
????template???
????Stack&?operator?=?(const?Stack&?obj);??
??
????T&?operator?[]?(const?int&?order);??
????T&?Top();??
????void?Push(const?T&?obj);??
????void?Pop();??
????bool?empty();??
};??
我們知道,當(dāng)我們?cè)诔蓡T函數(shù)中傳入的參數(shù)是類對(duì)象時(shí),是不能通過類對(duì)象直接訪問該對(duì)象的私有成員數(shù)據(jù)的。我們只能通過類類型提供的外設(shè)接口以某種方式訪問??對(duì)象的數(shù)據(jù)。,為了不改變傳入的類對(duì)象的原有屬性,將傳入的類對(duì)象完全復(fù)制到我們要操作的類對(duì)象中,我們進(jìn)行如下操作:
① 清除我們要操作的類對(duì)象中的數(shù)據(jù);
②創(chuàng)建與傳入對(duì)象類型相同的臨時(shí)變量,在定義時(shí)調(diào)用復(fù)制構(gòu)造函數(shù)將參數(shù)數(shù)據(jù)復(fù)制到臨時(shí)對(duì)象中;
③看到我提供給大家的成員函數(shù)列表中,*顯眼的有兩個(gè)函數(shù)Top和Pop。我們可以使用while循環(huán)先訪問臨時(shí)對(duì)象的頂部元素,將元素push_front移入容器,然后Pop釋放它。丟棄棧頂元素,迭代直到臨時(shí)變量中的元素為空;
④ 當(dāng)談到“如何判斷臨時(shí)對(duì)象中的元素是否為空?”時(shí),Stack類中的empty成員函數(shù)可以幫到我們。
說實(shí)話,當(dāng)我第一次遇到Stack自定義類的時(shí)候,我也納悶為什么不直接操作Stack的成員數(shù)據(jù)呢?Stack自定義類類型的成員數(shù)據(jù)的訪問權(quán)限設(shè)置為public不好嗎?事實(shí)上,這是反過來的。我們希望該程序僅以我們想要的方式運(yùn)行。我們可以對(duì)STL容器進(jìn)行處理,利用精華構(gòu)造出我們想要的“新容器”,但我們*關(guān)注的是“安全、安全、安全!” 只有經(jīng)過封裝,我們要提供的外設(shè)接口才暴露給操作者,才能保證程序的絕對(duì)安全!
模板專業(yè)化
其實(shí)模板特化就是我們常說的重載,但是我們這里的重載只是模板的類型參數(shù)列表不同,*重要的一點(diǎn)是“模板特化是針對(duì)類類型的”,函數(shù)模板沒有這個(gè)特征:
應(yīng)用于函數(shù)模板時(shí),編譯器報(bào)錯(cuò)!
模板全專業(yè)化
一個(gè)模板稱為全特化條件:1.必須有一個(gè)主模板類2.模板類型是完全指定的。
代碼示例:
#include???
using?namespace?std;??
??
template???
class?Base??
{??
public:??
????void?ShowInf()??
????{??
????????cout?<"實(shí)例化T的類型為"?<??
class?Base???
{??
public:??
????void?ShowInf()??
????{??
????????cout?<"全特化模板,實(shí)例化T的類型為int"?<?Obj;??
????Obj.ShowInf();??
}??
操作結(jié)果:
模板偏特化
偏特化是介于兩者之間的模板。它的模板名稱與主版本模板的名稱相同。但是,在其模板類型中,有明確的部分和未明確的部分。
注意:部分特化的條件:1.必須有主模板,2.模板類型部分明確。
類型參數(shù)為指針時(shí)模板的部分特化
代碼示例:
#include???
using?namespace?std;??
??
template???
class?Base??
{??
public:??
????void?ShowInf()??
????{??
????????cout?<"實(shí)例化T的類型為"?<??// 如果模板實(shí)例化參數(shù)T為指針類型,則調(diào)用該模板進(jìn)行實(shí)例化操作
class?Base???
{??
public:??
????void?ShowInf()??
????{??
????????cout?<"偏特化模板,其模板參數(shù)為"?<?Obj;??
????Obj.ShowInf();??
}??
操作結(jié)果:
類型參數(shù)部分實(shí)例化期間的部分模板特化
代碼示例:
#include???
using?namespace?std;??
??
template???
class?Base??
{??
public:??
????void?ShowInf()??
????{??
????????cout?<"普通模板的實(shí)例化"?<??
class?Base???
{??
public:??
????void?ShowInf()??
????{??
????????cout?<"偏特化模板"?<?Obj;??
????Obj.ShowInf();??
}??
操作結(jié)果:
當(dāng)兩個(gè)類型參數(shù) T1 和 T2 相同時(shí)模板的部分特化
代碼示例:
#include???
using?namespace?std;??
??
template???
class?Base??
{??
public:??
????void?ShowInf()??
????{??
????????cout?<"普通模板的實(shí)例化"?<??
class?Base???
{??
public:??
????void?ShowInf()??
????{??
????????cout?<"偏特化模板"?<?Obj;??
????Obj.ShowInf();??
}??
操作結(jié)果:
模板實(shí)例化時(shí),模板的全特化和模板的部分特化適用于調(diào)用序列:
注意:類類型一旦聲明為模板,就不能有同名的普通類的版本,即類模板和同名的普通類不能共存!
優(yōu)先級(jí):全專班>半專班>大師版模板班
代碼示例:
#include???
using?namespace?std;??
??
template???
class?Base??
{??
public:??
????void?ShowInf()??
????{??
????????cout?<"普通模板的實(shí)例化"?<??
class?Base??
{??
public:??
????void?ShowInf()??
????{??
????????cout?<"全特化模板"?<??
class?Base???
{??
public:??
????void?ShowInf()??
????{??
????????cout?<"偏特化模板"?<?Obj;??
????Obj.ShowInf();??
}??
操作結(jié)果:
函數(shù)模板的重載(類似于類模板的特化)
代碼示例:
#include???
using?namespace?std;??
??
template???
void?ShowInf(T?obj)??
{??
????cout?<"調(diào)用普通模板"?<??
void?ShowInf(int?obj)??
{??
????cout?<"調(diào)用特定類型的函數(shù)模板"?<
操作結(jié)果:
從這個(gè)例子可以看出,當(dāng)函數(shù)的普通版本和函數(shù)模板的專用版本都滿足要求時(shí),調(diào)用順序如下:
優(yōu)先順序:普通功能版>功能模板專業(yè)版>功能模板主版
模板“模板參數(shù)”
為什么需要模板的“模板參數(shù)”?
*重要的原因是“方便”!怎么方便?我們以 Stack 為例。如果我們想允許容器存儲(chǔ)指定的 Stack 元素,我們這樣做:
template?>???
class?Stack?{??
private:??
??Cont?elems;?//?elements??
??......??
};??
當(dāng)我們實(shí)例化一個(gè) Stack 類的對(duì)象時(shí),我們通常會(huì)這樣做:
Stack>?dblStack;??
但是這樣做的缺點(diǎn)是需要指定兩次元素類型,但是這兩種類型是一樣的。
使用模板模板參數(shù)允許我們?cè)诼暶?Stack 類模板時(shí)只指定容器的類型,而不指定容器中元素的類型。例如:
template??class?Cont?=?std::deque>??
class?Stack?{??
private:??
??Cont?elems;?//?elements??
public:??
??void?push(T?const?&);?//?push?element??
??void?pop();???????????//?pop?element??
??T?const?&top()?const;?//?return?top?element??
??bool?empty()?const?{??//?return?whether?the?stack?is?empty??
????return?elems.empty();??
??}??
??...??
};??
此時(shí),當(dāng)我們實(shí)例化 Stack 模板時(shí),我們執(zhí)行以下操作:
Stack?vStack;??????//?integer?stack?that?uses?a?vector??
與第一種方法的區(qū)別在于:第二個(gè)模板參數(shù)是一個(gè)類模板。
在這一點(diǎn)上,一切看起來都那么順利,真的嗎?
例如:
#include???
using?namespace?std;??
#include???
??
template??class?CONT?>??
class?Base??
{??
??
};??
??
int?main()??
{??
????Base?obj;??
}??
操作結(jié)果:
錯(cuò)誤原因:
其實(shí)參數(shù)不匹配是有原因的,我們傳入的參數(shù)是錯(cuò)誤的,為什么?讓我們回想一下:STL 標(biāo)準(zhǔn)容器類型實(shí)際上是一種模板類型。實(shí)例化一個(gè)STL容器的時(shí)候,通常是一個(gè)vector,但是vector容器里面其實(shí)有兩個(gè)參數(shù)。第二個(gè)參數(shù)是“容器空間配置器分配器”。為了完全匹配vector模板類的類型,我們做如下調(diào)整:
#include???
using?namespace?std;??
#include???
??
template??>?class?CONT?>??
class?Base??
{??
??
};??
??
int?main()??
{??
????Base?obj;??
}??
在這種情況下,程序運(yùn)行完全正常。
將字符串傳遞給模板的注意事項(xiàng)
有人問為什么錯(cuò)了?我們知道,適用字符數(shù)組的大小比較應(yīng)該遵循“字符數(shù)組中元素個(gè)數(shù)相同”的原則。這里的“apple”參數(shù)推導(dǎo)出的T是const char[6]類型(隱含'