2013-10-21 41 views
2

請理解,我仍然在學習C++ 11的移動和右值語義的概念。我的問題是遺留代碼是否可以通過簡單地使用C++ 11編譯器和STL免費獲得避免不必要拷貝的午餐。C++ 11右值和移動:遺留代碼是否可避免複製?

這是一個非常簡單的例子。此代碼爲給定的字符串構建一個簡單的字符頻率表。例如,「apple」應返回{('a', 1), ('e', 1), ('l', 1), ('p', 2)}。你會看到,我只是使用向量作爲值。

typedef std::tuple<char, int> Frequency; 
typedef std::vector<Frequency> Frequencies; 

Frequencies buildFrequenciesTable(std::string w) { 
    char table['z' - 'a' + 1] = { 0, }; 
    std::for_each(w.cbegin(), w.cend(), [&table](char c) { 
    ++table[::tolower(c) - 'a']; 
    }); 

    Frequencies freqs; 
    for (size_t i = 0; i < 'z' - 'a' + 1; ++i) { 
    if (table[i] != 0) 
     freqs.push_back(tuple<char, int>((char) ('a' + i), table[i])); 
    } 
    return freqs; // Q1: Is vector get copied? 
} 

int main() { 
    using namespace std; 

    Frequencies f1 = buildFrequenciesTable("apple"); // Q2: Copy? 
    Frequencies f2 = buildFrequenciesTable("banana"); 
    vector<Frequencies> fs = { f1, f2 }; // Q3: Copy? 
} 

清楚的是,C++ 03返回向量作爲值時生成所有拷貝代碼(使用拷貝構造函數和賦值運算符)。在C++ 11中怎麼樣? std::vector a有移動構造函數。這段代碼能避免任何unnessarry副本嗎?或者,我應該在上面的代碼中使用&&還是std::forward

我試圖調試內部的STL代碼,但很難說服。

注:我的目標是最大限度地減少這些功能中不必要的副本。我知道我可以使用新的/指針/引用,但這將需要解決內存泄漏問題。所以,我想盡可能地使用價值。

回答

5

對於Q1,由於「命名返回值優化」(NRVO)刪除副本,所以即使在C++ 03中也很可能沒有副本。

對於Q2,即使在C++ 03中也很可能沒有拷貝,因爲copy-elision刪除它。

對於Q3,即使在C++ 11你有副本,你會需要標記f1f2如移動,以便真正讓他們感動:

vector<Frequencies> fs = { std::move(f1), std::move(f2) }; 

既然你問多個問題我想我會省略進一步的解釋,查找NRVO,copy-elision和std::move是必需的,詢問您是否還有其他問題。

然而,有這樣的情況,你會得到免費的移動,例如,如果有一個臨時可能被移動:

vector<Frequencies> fs = { buildFrequenciesTable("apple"), 
          buildFrequenciesTable("bananas") }; 

以上將檢測這兩個矢量從buildFrequenciesTable()返回臨時變量,因此,他們將被移至fs

+0

謝謝!那麼,基本上,在Q1和Q2的情況下,C++ 11的r值引用和移動語義沒有什麼好處?我的理解正確嗎? – Nullptr

+0

我的目標實際上是儘量減少這些功能中不必要的副本。我知道我可以使用新的/指針/引用,但這將需要瘋狂的泄漏問題。所以,我想盡可能地使用價值。 – Nullptr

+0

還有一個子問題(可能是個愚蠢的問題):我可以做更多的優化代碼嗎?例如,在'buildFrequenciesTable'中返回'Frequencies &&',或者聲明'Frequencies && f1'左右。特別是,上面的'f1'和'f2'不會被修改。 – Nullptr

1

從函數返回向量(Q1)將盡可能使用移動語義,而無需修改代碼。同樣,從返回的臨時(Q2)初始化向量將使用移動語義;返回值是右值,所以可以從中移出。實際上,這兩種移動(或者歷史上的副本)都應該被省略,以便該功能直接初始化f1f2而不移動或複製。

將它們放在向量(Q3)中的確需要複製:變量是左值,它不能被隱式移動。所以你將不得不使用std::move,或重新構建代碼,以避免這些副本。