之前遇到一個使用情況,在unique_ptr已經存在 於STL Container當中,想要把他從Container移除且回傳出被分離的unique_ptr,該怎麼做。
Original idea 原來的想法很簡單,沒考慮太多
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <memory> #include <vector> #include <algorithm> #include <iostream> using namespace std; template<typename Func> unique_ptr<int> RemoveFromContainer(vector<unique_ptr<int>> &c, Func pred) { auto it = find_if(begin(c), end(c), pred); unique_ptr<int> ret = move(*it); c.erase(it); return ret; } int main() { vector<unique_ptr<int>> vec; vec.push_back(make_unique<int>(1)); vec.push_back(make_unique<int>(2)); vec.push_back(make_unique<int>(3)); unique_ptr<int> ret = RemoveFromContainer(vec, [](const unique_ptr<int> &elem) { return *elem == 2; }); if (ret) cout << *ret << endl; for (auto &elem : vec) cout << *elem << endl; }
注意要將unique_ptr從Container移出來需要用move,然後才能將其刪除。
More Generic 為了讓這個函數更Generic,做了一點嘗試
用 auto / decltype來作回傳質推導 傳進來的參數不在是vector<unique_ptr<int>>
了,直接讓編譯器去推導 然後寫了以下的Code
1 2 3 4 5 6 auto RemoveFromContainer(Cond &c, Func pred) -> decltype(move(*begin(c))) { auto it = find_if(begin(c), end(c), pred); auto ret = move(*it); c.erase(it); return move(ret); }
悲劇發生了,原本該印出來的2不見了。而資料也被從Container咦除了。 原因是在於我們回傳型態不對。 一開始的版本,我們是回傳一個Value Semantics: unique_ptr<T>
。 而這個版本加上move
之後,就變成了 unique_ptr<T>&&
。 由於錯誤的回傳型態,然後這個unique_ptr就佚失了。更盛著程式直接Crash。
改良版 試了半天之後發現這樣可以用
1 2 3 4 5 6 7 template <typename Cond, typename Func> typename Cond::value_type RemoveFromContainer(Cond &c, Func pred) { auto it = find_if(begin(c), end(c), pred); auto ret = move(*it); c.erase(it); return ret; }
因為STL container有value_type紀錄當前value的type,所以剛好可以使用。 也許有更好得方法,有時間再來研究。