[英]C++11: call by value, move semantics and inheritance
假設我有一個類,我打算將它作為可實例化的類直接暴露給程序員:
class Base
{
public:
Base(std::string text) : m_text(std::move(text)) {}
private:
std::string m_text;
};
到現在為止還挺好。 這里不需要rvalue構造函數。 現在,在未來的某個時刻,我決定擴展Base:
class Derived : public Base
{
public:
Derived(const std::string &text) : Base(text) {}
};
這讓我感到困惑:我無法在Derived中按值獲取字符串,因為這就是Base已經在做的事情 - 我最終會得到2個副本和1個移動。 這里的const-reference構造函數還對rvalues執行不必要的復制。
問題是:如何復制+僅移動一次(就像Base中的簡單構造函數一樣)而不添加更多構造函數?
您不能只復制和移動一次,除非您更改類的設計並將其構造函數轉換為(可能是SFINAE約束的)模板化轉發構造函數( Yakk的答案顯示如何 )。
雖然這樣做可以在提供rvalues時只執行一次移動而不執行復制,並且在提供左值時只執行一次復制而不移動,但在大多數情況下這是一種過度殺傷。
作為基於模板的轉發構造函數的替代方法,您可以在基類和派生類中提供兩個構造函數:一個用於rvalue引用,另一個用於對const
lvalue引用。 但同樣,這在大多數情況下都是不必要的並發症(並且當參數數量增加時不能很好地擴展,因為所需構造函數的數量將呈指數增長)。
移動std::string
與復制指針和整數一樣快(這里忽略SSO優化),除非你有真實證據證明這是阻止你的應用程序滿足其性能要求的瓶頸,否則你不應該為此煩惱(難以置信)。
因此,只需讓Derived
構造函數無條件地通過值獲取其參數,並在將其傳遞給基類的構造函數時移動它:
class Derived : public Base
{
public:
Derived(std::string text) : Base(std::move(text)) { }
};
如果你想要(或接受) Derive
繼承Base
的所有構造函數,另一個選擇是利用C ++ 11的繼承構造函數,如下所示:
class Derived : public Base
{
public:
using Base::Base;
// ^^^^^^^^^^^^^^^^^
};
使用SFINAE構造函數完美轉發:
class Derived : public Base
{
public:
template<
typename T,
typename=typename std::enable_if<
std::is_constructible<std::string, T&&>::value
>::type
>
Derived(T&& text) : Base(std::forward<T>(text)) {}
};
一個缺點(超出上述結構的瘋狂)是這將參與重載解析到一個過於貪婪的程度:它幾乎完美匹配任何可以轉換為std::string
。 因此,如果你有另一個帶有char const*
構造函數,如果你傳入一個char const*
變量,它可能會被忽略,因為T&&
可能更好地匹配。
如果您的Derived
或Base
有一個operator std::string
那么你會感到很遺憾......
更改Derived
到以下內容:
class Derived : public Base
{
public:
using Base::Base;
};
現在Derived
從Base
繼承構造函數。
有關更多信息,請參閱在C ++ 0x中轉發所有構造函數 。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.