2013-10-13 62 views
2

我試圖將數據放入std::map。下面是我嘗試(從原始來源修剪,但肯定給的想法):std :: map :: emplace語法的問題

template<typename T> class trie { 

private: 
std::map<typename T::value_type, std::unique_ptr<trie<T>>> children; 
std::unique_ptr<trie<T>> parent; 

// Later 
public: 
trie(const trie<T>& other, trie<T>* const parent) : 
parent{parent} 
{ 
    for(auto const &it : other.children) 
     children.emplace(it.first, {*it.second}); 
} 

}; 

錯誤如下:

trie.h: In instantiation of ‘trie<T>::trie(const trie<T>&, trie<T>*) [with T = std::basic_string<char>]’: 
main.cpp:7:23: required from here 
trie.h:90:3: error: no matching function for call to ‘std::map<char, std::unique_ptr<trie<std::basic_string<char> >, std::default_delete<trie<std::basic_string<char> > > >, std::less<char>, std::allocator<std::pair<const char, std::unique_ptr<trie<std::basic_string<char> >, std::default_delete<trie<std::basic_string<char> > > > > > >::emplace(const char&, <brace-enclosed initializer list>)’ 
    children.emplace(it.first, {*it.second}); 
^
trie.h:90:3: note: candidate is: 
In file included from /usr/include/c++/4.8.1/map:61:0, 
       from trie.h:4, 
       from main.cpp:2: 
/usr/include/c++/4.8.1/bits/stl_map.h:540:2: note: std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::emplace(_Args&& ...) [with _Args = {}; _Key = char; _Tp = std::unique_ptr<trie<std::basic_string<char> >, std::default_delete<trie<std::basic_string<char> > > >; _Compare = std::less<char>; _Alloc = std::allocator<std::pair<const char, std::unique_ptr<trie<std::basic_string<char> >, std::default_delete<trie<std::basic_string<char> > > > > >; typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename _Alloc::rebind<std::pair<const _Key, _Tp> >::other>::iterator = std::_Rb_tree_iterator<std::pair<const char, std::unique_ptr<trie<std::basic_string<char> >, std::default_delete<trie<std::basic_string<char> > > > > >] 
    emplace(_Args&&... __args) 
^
/usr/include/c++/4.8.1/bits/stl_map.h:540:2: note: candidate expects 0 arguments, 2 provided 

所以我的問題是:

如何我是否正確初始化地圖元素,目標是指向樹的深層副本,並且沒有不必要的副本/移動?

在此先感謝!

+0

是不是'* it.second'解析爲對'trie '的引用?你不能從那裏初始化一個'unique_pointer'。 – juanchopanza

+0

在類聲明中刪除'trie ::'是額外限定的。 – deepmax

+0

這是你的真實密碼嗎? – billz

回答

4

通過傳遞{*it.second}作爲值的初始值,您可以有效地嘗試使用trie<T>初始化std::unique_ptr<trie<T>>。我相信你正在尋找這樣的:

public: 
trie(const trie<T>& other, trie<T>* const parent) : 
parent{parent} 
{ 
    for(auto const &it : other.children) { 
     // Separate creation of unique_ptr for exception safety, thanks to @DanielFrey 
     std::unique_ptr<trie<T>> p(new trie<T>(*it.second)); 
     children.emplace(it.first, std::move(p)); 
    } 
} 

注意,你還必須提供一個拷貝構造函數,因爲默認的一個被刪除,因爲你的類具有不可複製的成員。


與問題無關,但您應該重新考慮您的設計:您最有可能擁有所有權迴路。如果trie<T>向其子女存儲unique_ptr,並且這些子女將unique_ptr存儲回父母,則會發生雙重刪除錯誤。將其中的一個(可能是父指針)轉換爲一個原始指針。沒有參與所有權的情況下,原始指針可以觀察。

+0

我認爲當'emplace'引發時你創建了一個資源泄漏。看到我的答案是真正需要的。 –

+0

@DanielFrey好點。 – Angew

+0

優秀的答案。關於所有權循環的好處;我沒有想到這一點。如果我在聲明中設置了「parent = nullptr」,這個函數可以作爲拷貝構造函數嗎?另外,是否沒有辦法在原地構建'unique_ptr'?我看@ DanielFrey的解釋,但我想我也許可以用'{}'傳遞給構造函數。 – thirtythreeforty

4

你需要

for(auto const &it : other.children) { 
    std::unique_ptr<trie<T>> element(new trie<T>(*it.second)); 
    children.emplace(it.first, std::move(element)); 
} 

,以防止萬一異常是從emplace拋出資源泄漏。如果可用(C++ 14),可以簡化代碼

for(auto const &it : other.children) { 
    children.emplace(it.first, std::make_unique<trie<T>>(*it.second)); 
} 

根據經驗,對所有智能指針的規則,你總是使用std::make_*你必須使用一個單獨的行來創建他們每個人。

+0

爲'make_unique'語法+1 - 非常乾淨!它是否更快,因爲編譯器將會返回值? (不幸的是,這個問題有點沒有實際意義,因爲我不打算使用草案標準。) – thirtythreeforty

+1

@thirtythreeforty'make_unique'實際上很簡單,它是一個簡單的監督,它不包含在C++ 11中。實際上它非常簡單,您可以自己編寫並將其添加到代碼中。你可以在[這個問題]中找到它(http://stackoverflow.com/questions/7038357)。這不是更有效率,但更多的例外安全,你可以在我的答案中看到。 –

+0

@thirtythreeforty只是爲了澄清:使用'make_unique'將最有可能像現場構建一樣高效,因爲它只是一個單一的指針,我很確定優化器將能夠消除所有開銷。 –