Fluent C++:Mixin類——CRTP的陽面

原文

既然我們已經(jīng)清楚了CRTP的工作原理赁咙,那么讓我與你分享另一種涉及模板的技術(shù),該模板是CRTP的補充:Mixin類瓣俯。

我發(fā)現(xiàn)Mixin類很有趣俊马,因為它們?yōu)镃RTP提供了另一種實現(xiàn)等效的方法,因此提供了不同的權(quán)衡敷存。

CRTP的主要用途是為特定類添加通用功能住拭。 Mixin類也這樣做。

Mixin類是定義通用行為的模板類,通過繼承你希望擴展其功能的類型來實現(xiàn)滔岳。

這兒有一個例子。 讓我們上一個代表一個人的名字的類挽牢。 它具有名字和姓氏谱煤,并且可以使用特定格式打印出該名字:

class Name
{
public:
    Name(std::string firstName, std::string lastName)
      : firstName_(std::move(firstName))
      , lastName_(std::move(lastName)) {}

    void print() const
    {
        std::cout << lastName_ << ", " << firstName_ << '\n';
    }

private:
    std::string firstName_;
    std::string lastName_;
};

這兒是使用它的代碼段:

Name ned("Eddard", "Stark");
ned.print();

這會輸出:

Stark, Eddard

到目前為止,還沒有什么特別的禽拔,但是這兒有一個新的需求:我們需要能夠連續(xù)多次打印此名稱刘离。

我們可以向Name類添加一個repeat方法。 但是睹栖,重復(fù)調(diào)用print方法的概念也可以應(yīng)用于其他類硫惕,例如PhoneNumber類,也可以具有print()方法野来。

mixin類的想法是將通用功能隔離到其自己的類中恼除,使用要增加該功能的類型對該類進行模板化,并從該類型派生:

template<typename Printable>
struct RepeatPrint : Printable
{
    explicit RepeatPrint(Printable const& printable) : Printable(printable) {}
    void repeat(unsigned int n) const
    {
        while (n-- > 0)
        {
            this->print();
        }
    }
};

在我們的示例中曼氛,Name類將扮演Printable的角色豁辉。

注意repeat方法的實現(xiàn)中的this->。 沒有它舀患,代碼將無法編譯徽级。 確實,編譯器不確定在哪里聲明的print:即使在模板類Printable中聲明了它聊浅,從理論上講餐抢,也無法保證該模板類不會被特化并針對特定類型進行重寫,從而不會公開print方法 低匙。 因此旷痕,C ++中會忽略模板基類中的名稱。

使用this->是將它們重新包含在調(diào)用它們的函數(shù)范圍內(nèi)的一種方法努咐。 還有其他方法也可以做到苦蒿,盡管它們可能并不適合這種情況。 無論如何渗稍,你都可以在Effective C++ 的第43條中閱讀有關(guān)此主題的所有信息佩迟。

為了避免顯式指定模板參數(shù),我們使用一個推導(dǎo)它們的函數(shù):

template<typename Printable>
RepeatPrint<Printable> repeatPrint(Printable const& printable)
{
    return RepeatPrint<Printable>(printable);
}

然后這兒是我們的客戶端代碼:

Name ned("Eddard", "Stark");    
repeatPrint(ned).repeat(10);

輸出就變成了:

Stark, Eddard
Stark, Eddard
Stark, Eddard
Stark, Eddard
Stark, Eddard
Stark, Eddard
Stark, Eddard
Stark, Eddard
Stark, Eddard
Stark, Eddard

我們甚至可以改個名字來讓代碼更有表現(xiàn)力:

Name ned("Eddard", "Stark");    
repeatedlyPrint(ned).times(10);

(我在這里改名稱竿屹,只是為了跟之前的CRTP代碼進行比較报强,在那里這些新名稱并不適用。)

CRTP的反面

Mixin類涉及模板和繼承的混合拱燃,以便將通用功能插入現(xiàn)有類秉溉。 這種感覺就像CRTP,不是嗎?

Mixin類類似于CRTP召嘶,但是是反過來的父晶。 實際上,我們的mixin類如下所示:

class Name
{
    ...
};

template<typename Printable>
struct RepeatPrint : Printable
{
    ...
};

repeatPrint(ned).repeat(10);

而相應(yīng)的CRTP則看起來像這樣:

template<typename Printable>
struct RepeatPrint
{
   ...
};

class Name : public RepeatPrint<Name>
{
    ...
};

ned.repeat(10);

實際上弄跌,這是使用CRTP的解決方案的完整實現(xiàn):

template<typename Printable>
struct RepeatPrint
{
    void repeat(unsigned int n) const
    {
        while (n-- > 0)
        {
            static_cast<Printable const&>(*this).print();
        }
    }
};

class Name : public RepeatPrint<Name>
{
public:
    Name(std::string firstName, std::string lastName)
      : firstName_(std::move(firstName))
      , lastName_(std::move(lastName)) {}

    void print() const
    {
        std::cout << lastName_ << ", " << firstName_ << '\n';
    }

private:
    std::string firstName_;
    std::string lastName_;
};

int main()
{
    Name ned("Eddard", "Stark");    
    ned.repeat(10);
}

那么甲喝,CRTP還是mixin類?

CRTP和mixin類提供了解決同一問題的兩種方法:向現(xiàn)有類添加通用功能,但要權(quán)衡取舍铛只。

以下是它們之間的不同點:

CRTP:

  • 影響現(xiàn)有類的定義埠胖,因為它必須繼承自CRTP,
  • 客戶代碼直接使用原始類淳玩,并從其擴展的功能中受益直撤。

mixin類:

  • 保持原始類不變,
  • 客戶代碼不會直接使用原始類,而是需要將其包裝到mixin中才能使用擴展功能蜕着,
  • 即使沒有虛析構(gòu)函數(shù)谋竖,它也會從原始類繼承。你可以這么干侮东,除非要通過指向原始類的指針多態(tài)刪除mixin類圈盔。

了解這些權(quán)衡之后,你可以選擇最適合給定情況的解決方案悄雅。

CRTP不僅限于此驱敲。如果你想了解更多信息,我已經(jīng)為CRTP撰寫了一個系列宽闲,該系列已變得很火众眨。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市容诬,隨后出現(xiàn)的幾起案子娩梨,更是在濱河造成了極大的恐慌,老刑警劉巖览徒,帶你破解...
    沈念sama閱讀 222,183評論 6 516
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件狈定,死亡現(xiàn)場離奇詭異,居然都是意外死亡习蓬,警方通過查閱死者的電腦和手機纽什,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 94,850評論 3 399
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來躲叼,“玉大人芦缰,你說我怎么就攤上這事》憧叮” “怎么了让蕾?”我有些...
    開封第一講書人閱讀 168,766評論 0 361
  • 文/不壞的土叔 我叫張陵浪规,是天一觀的道長。 經(jīng)常有香客問我探孝,道長笋婿,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 59,854評論 1 299
  • 正文 為了忘掉前任再姑,我火速辦了婚禮萌抵,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘元镀。我一直安慰自己,他們只是感情好霎桅,可當我...
    茶點故事閱讀 68,871評論 6 398
  • 文/花漫 我一把揭開白布栖疑。 她就那樣靜靜地躺著,像睡著了一般滔驶。 火紅的嫁衣襯著肌膚如雪遇革。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 52,457評論 1 311
  • 那天揭糕,我揣著相機與錄音萝快,去河邊找鬼。 笑死著角,一個胖子當著我的面吹牛揪漩,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播吏口,決...
    沈念sama閱讀 40,999評論 3 422
  • 文/蒼蘭香墨 我猛地睜開眼奄容,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了产徊?” 一聲冷哼從身側(cè)響起昂勒,我...
    開封第一講書人閱讀 39,914評論 0 277
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎舟铜,沒想到半個月后戈盈,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 46,465評論 1 319
  • 正文 獨居荒郊野嶺守林人離奇死亡谆刨,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 38,543評論 3 342
  • 正文 我和宋清朗相戀三年塘娶,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片痴荐。...
    茶點故事閱讀 40,675評論 1 353
  • 序言:一個原本活蹦亂跳的男人離奇死亡血柳,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出生兆,到底是詐尸還是另有隱情难捌,我是刑警寧澤膝宁,帶...
    沈念sama閱讀 36,354評論 5 351
  • 正文 年R本政府宣布,位于F島的核電站根吁,受9級特大地震影響员淫,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜击敌,卻給世界環(huán)境...
    茶點故事閱讀 42,029評論 3 335
  • 文/蒙蒙 一介返、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧沃斤,春花似錦圣蝎、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,514評論 0 25
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至哮针,卻和暖如春关面,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背十厢。 一陣腳步聲響...
    開封第一講書人閱讀 33,616評論 1 274
  • 我被黑心中介騙來泰國打工等太, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人蛮放。 一個月前我還...
    沈念sama閱讀 49,091評論 3 378
  • 正文 我出身青樓缩抡,卻偏偏與公主長得像,于是被迫代替她去往敵國和親筛武。 傳聞我的和親對象是個殘疾皇子缝其,可洞房花燭夜當晚...
    茶點故事閱讀 45,685評論 2 360

推薦閱讀更多精彩內(nèi)容