2012-10-17 65 views
2

我試圖評估右值引用如何影響類的設計。說我有一個現有的類如下所示右值引用和類設計

class X 
{ 
    string internal; 

public: 
    void set_data(const char* s) 
    { 
     internal = s; 
    } 
.. 
.. 
.. 
//other stuff 

}; 

此類用於由另一個模塊是這樣的:

//another module 
{ 

    string configvalue; 
    X x; 

    //read configvalue from a file and call set 

    ... 
    x.set_data(configvalue.c_str()); 

    //use x to do some magic 
    .. 
    ... 


} 

在適當位置rvalue引用將是更好,以提供另一成員函數,像這樣

class X 
{ 
... 
... 
.... 
void set_data(string s) 
{ 
    internal = std::move(s); 
} 
}; 

這將允許此類的客戶端使用移動語義並防止每次使用一組分配/複製操作。這是一個高度炮製的例子,但是同樣的原則適用於所有類設計而不會打破「最小接口」範例。

有關此事的任何人的見解都非常感激?

回答

-1

我沒有看到將void set_data(const char* s)void set_data(string s)作爲接口一部分的理由。這會造成含糊不清,容易產生副作用。此外,您仍然可以致電set_data(string s),通過價值傳遞論據。相反,我會建議定義2個以下funcs中:

void set_data(const string &s); 
void set_data(string &&s); 

這樣你可以有2個實現,首先將深複製你的字符串,第二個可以竊取該字符串的內部,因爲它是一個rvalue(請務必留下它處於已定義的狀態,因此析構函數將能夠毫無問題地銷燬它 - 詳情請參見http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2006/n2027.html#Move_Semantics)。

第二個版本將自動調用rvaluestring參數或者參數被強制爲rvalue,例如std::move

如果你想要有一個按值選項,你可以使用這個API的rvalue版本和字符串拷貝構造函數:set_data(string(str))

+1

你編碼了你的建議嗎? –

+0

@霍華德號不是嗎? – SomeWittyUsername

+0

是的。當我實現你的前兩個'set_data'並用右值'string'調用時,我的編譯器抱怨含糊不清。當我添加你的第三個'set_data'重載時,我用左值'string'調用時也會產生歧義。 –

4

是的,根據您的建議添加string重載是個不錯的主意。即使沒有右值引用,這樣的重載也是一個好主意。否則,給定一個std::string s,使用它必須:

x.set_data(s.c_str()); 

x.set_data(s); 

是的X的客戶端,以便更直觀(甚至會更有效)。

作爲另一個選項,你可以添加這兩種重載:

void set_data(const string& s) {internal = s;} 
void set_data(string&& s)  {internal = std::move(s);} 

這大致相當於你正確的建議單過載。雙重過載解決方案的性能優勢很小。當傳遞的參數是一個xvalue(一個已經與std::move一起投射的左值)時,單重載解決方案將花費額外的string移動構造。但std::string的移動構造函數應該非常快,所以這應該不是什麼大問題。我只是在充分披露的精神下提到這一點。

如果set_data有多個參數,「按值」方法變得更有吸引力。例如,考慮您需要通過兩個string s的情況。你的選擇是:

解決方案1個

void set_data(string s1, string s2); 

解決方案2

void set_data(const string& s1, const string& s2); 
void set_data(  string&& s1, const string& s2); 
void set_data(const string& s1,  string&& s2); 
void set_data(  string&& s1,  string&& s2); 

正如你可以很快地看到,解決方案2個尺度不好用的參數的數量。

最後,在任何情況下,你應該嘗試這兩種解決方案適用於同類型:

不要這樣做!

void set_data(string s)  {internal = std::move(s);} 
void set_data(const string& s) {internal = s;} 
void set_data(string&& s)  {internal = std::move(s);} 

這組重載將是不明確的。正如在C++ 03以下兩個重載是不明確的:

void set_data(string s)  {internal = std::move(s);} 
void set_data(const string& s) {internal = s;} 

決不過載按值參考,無論是左值參考也不右值參考。

+0

'void set_data(const string&s){internal = s;} void set_data(string && s){internal = std :: move(s);}',什麼時候會第一次被調用,什麼時候會第二次? –

+0

第一個版本更喜歡綁定到左值......第二個版本更喜歡綁定到rvalues以及用std :: move包裝的左值。 – Jason

+0

關於您的多個參數傳遞 - 是的,它的規模很小 - 但這不應該是選擇「按價值」方法的原因。它可以很容易解決(可能應該是出於設計考慮),通過將參數存儲在單個配置對象中,該配置對象將通過引用傳遞給重載函數 - 將問題簡化爲具有單一參數的原始問題 – SomeWittyUsername