0%

Transfer unique_ptr ownership from STL container

之前遇到一個使用情況,在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,所以剛好可以使用。
也許有更好得方法,有時間再來研究。