2014-01-23 93 views
0

我有一行只在一次計算中重複使用結構類型變量,其結果被分配回變量本身。如何使用c + + 11移動語義明確避免複製

for (int i = 0; i < 100; i++) 
     e = f(i,e); //how to avoid copying 

整個代碼如下。我的問題是,

如何明確確保e內的數據未被複制多次。

移動語義是一個很好的選擇嗎?

如何使用移動比較定義e作爲一個普通的非常量引用?

#include <vector> 
using namespace std; 
struct arr { 
    vector<int> data; 
    int len; 
}; 

arr f(int x,arr xs) { 
    xs.data.push_back(x); 
    return xs; 
} 

arr g() { 
    arr e; 
    for (int i = 0; i < 100; i++) 
     e = f(i,e); //how to avoid copying 
    return e; 
} 
int main() { 
    auto res = g(); return 0; 
} 

---編輯---

最接近於我所尋找的是在有關更改至e = f(i,std::move(e))評論。根據我的理解,它明確告訴編譯器(第二次出現)e不再需要,它的資源可以被採用,我猜這是編譯器無法推斷並且必須被告知的。

我以這種方式編寫示例代碼的原因是我想避免在void f中使用非const引用,並且在不犧牲效率的情況下獲得非副作用代碼的美觀/錯覺。我認爲這是移動語義學購買我們的一件事,對吧? (允許標準容器被「複製」)。我僞造了len字段和struct來做個例子,因爲stl容器已經有了所有的移動語義處理程序。

鑑於我從答案學會爲止,我的問題其實歸結爲: 之前,我在代碼中使用e = f(i,std::move(e))後,有多少vector複製操作真的發生了?它是沒有或100?爲什麼?

我需要聲明farr f(int x,arr&& xs) {,爲什麼或爲什麼不需要使用&&

+0

爲什麼不通過引用將arr傳遞給f? –

+1

'e = f(i,std :: move(e));' – qwm

回答

1

如果我理解這個問題,你可以使用指針或引用

3

如何明確地確保內部E中的數據沒有通過多次複製。

通過引用傳遞arr。

移動語義是一個很好的選擇嗎?

在這種情況下,這不是一個好的選擇。你把結果賦給e(所以它會起作用)。問題是如果你在f中做了一些會導致失敗的東西(拋出異常)。在這種情況下,您不僅會丟失所做的更改,而且還會丟失e(因爲它的值將被移至f,但不會返回)。

如何使用move比較將e定義爲簡單的非常量引用?

使用移動意味着您創建了一個高效的破壞性副本(原始操作後爲空)。

+0

但不會f()通過回到循環返回e? – marcinj

+0

也OP不能「通過const引用傳遞arr」。因爲它在f – marcinj

+0

中被修改感謝您的發現。我糾正了我的答案。 – utnapistim

0

如何明確確認e中的數據不會被複制很多次 以上。

移動語義是一個很好的選擇嗎?

不,至少不是這種情況。您需要通過引用採取數組:

#include <algorithm> 
#include <vector> 
using namespace std; 
struct arr { 
    vector<int> data; 
    int len; 
}; 

void f(arr& a, int x) { 
    a.data.push_back(x); 
} 

arr g() { 
    arr e; 
    e.data.reserve(100); 
    for (int i = 0; i < 100; i++) 
     f(e, i); 
    return e; 
} 

int main() { 
    auto res = g(); return 0; 
} 

如果您知道的元素(100在你的例子)的數量,你可能要預留空間,對他們來說,這是更有效的方式。

0

作爲utnapistim回答的補充,我爲您提供了一種可以實現相同結果的替代方法,即使用C++ 11語言和庫函數向n個連續整數值填充矢量。

鑑於你顯然要組一個vectorint,這似乎是用來存儲數組的大小,儘可能短的版本,我可以拿出並且是

#include <vector> 
#include <algorithm> 

struct arr { 
    std::vector<int> data; 
    int len; 
}; 

int main() { 
    arr a; 
    std::generate_n(std::back_inserter(a.data), 100, [&]() -> int { return a.data.size(); }); 
    return 0; 
} 

arr::data的構造沒有辦法。假設您無法估計將要推送的元素數量,在推送之前調整其大小並不是一個選項。儘管如此,你可以簡單地使用 push_back(通過使用std::back_inserter)由生成器函數生成的n個值(正如你所看到的,它可以是一個整潔的小lambda)。實際上,您可以顯着減少代碼和問題。

上述解決方案避免了原密碼多個問題:

a)你不必調用不必要的功能f() - 它只是做了push_back,你可以只是還直接寫在g()東西。定義和調用f()只是完全沒有必要,但最糟糕的部分是,你想每次都複製arr你想push_back一個整數。這意味着你必須每次構建一個新的vector複製原來存在的任何東西,這意味着你存儲在你的向量中的任何類型的n個結構。

b)您不必編寫顯式循環並手動調用push_back。這使得不需要功能g()

c)最後和隨後,您避免函數調用g()。使用返回值g()初始化res通常不會造成問題,因爲返回值優化不需要複製g()中的本地vector(這正是初始化res時所做的)。然而,與性能優化相比,這更像是一種代碼減少 - 您只需要編寫和維護一個函數。

0

每對這個問題qwm的評論,使用

e = f(i,std::move(e)); 

會救你一個副本,而兩個拷貝的預期實際上移動。

  • 通過複製
  • 通過複製返回傳遞參數(實際上移動的構造函數)
  • 分配回你的對象(實際移動分配)

還爲給G返回一個舉動。

但正如其他答案所述,請使用傳遞引用。或者直接使用vector來代替struct(向量知道它們自己的長度)。順便說一句,如果事先知道你的向量將保存多少個元素,只需設置向量的大小並使用一個循環來分配像你一樣的元素的數組。由於每次調用都會檢查容量,因此使用push_back會有開銷。