2016-07-05 72 views
4

一個如何初始化靜態映射,其中值是std::unique_ptr初始化靜態的std ::地圖的unique_ptr作爲價值

static void f() 
{ 
    static std::map<int, std::unique_ptr<MyClass>> = { 
     { 0, std::make_unique<MyClass>() } 
    }; 
} 

當然,這並不工作(的std::unique_ptr拷貝構造函數被刪除)。

這可能嗎?

+0

你必須爲一件事提供一個變量名稱。 – Galik

回答

5

的問題是,從std::initializer-list複製其內容建設。 (std::initializer_list中的對象本質上是const)。 解決您的問題:您可以從一個單獨的函數初始化地圖...

std::map<int, std::unique_ptr<MyClass>> init(){ 
    std::map<int, std::unique_ptr<MyClass>> mp; 
    mp[0] = std::make_unique<MyClass>(); 
    mp[1] = std::make_unique<MyClass>(); 
    //...etc 
    return mp; 
} 

然後調用它

static void f() 
{ 
    static std::map<int, std::unique_ptr<MyClass>> mp = init(); 
} 

看到它Live On Coliru

+0

如果'init()'會返回'std :: map <...> &&'(rvalue)會有區別嗎?即返回一個右值會更好嗎?更新:與右值將有一個段錯誤 – vladon

+1

@ vladon,這不僅僅是一個性能perssimization,這也是一個錯誤。您將返回一個對被銷燬對象的「右值」引用。參見[this](http://stackoverflow.com/questions/4986673/c11-rvalues-and-move-semantics-confusion-return-statement)...在這種情況下,按值返回是非常有效的,因爲它的[肯定(C++ 17)](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2015/p0135r0。 html)將享受[RVO](http://en.cppreference.com/w/cpp/language/copy_elision) – WhiZTiM

1

另一種方式做,這是使用lambda。它與使用單獨的函數相同,但將地圖的初始化更接近動作。在這種情況下,我使用了自動&和decltype的組合來避免必須命名地圖的類型,但這僅僅是爲了好玩。

注意,傳遞到拉姆達的說法是到尚未在調用點構造一個對象的引用,所以我們不能引用它以任何方式。它只用於類型扣除。

#include <memory> 
#include <map> 
#include <utility> 

struct MyClass {}; 


static auto& f() 
{ 
    static std::map<int, std::unique_ptr<MyClass>> mp = [](auto& model) 
    { 
    auto mp = std::decay_t<decltype(model)> {}; 
    mp.emplace(0, std::make_unique<MyClass>()); 
    mp.emplace(1, std::make_unique<MyClass>()); 
    return mp; 
    }(mp); 
    return mp; 
} 

int main() 
{ 
    auto& m = f(); 
} 

這是另一種方法。在這種情況下,我們已經通過一個臨時的lambda並依賴copy elision/RVO。

#include <memory> 
#include <map> 
#include <utility> 

struct MyClass {}; 

static auto& f() 
{ 
    static auto mp = [](auto mp) 
    { 
    mp.emplace(0, std::make_unique<MyClass>()); 
    mp.emplace(1, std::make_unique<MyClass>()); 
    return mp; 
    }(std::map<int, std::unique_ptr<MyClass>>{}); 
    return mp; 
} 

int main() 
{ 
    auto& m = f(); 
} 

而又一種方式,在可變lambda中使用lambda捕獲。

#include <memory> 
#include <map> 
#include <utility> 

struct MyClass {}; 

static auto& f() 
{ 
    static auto mp = [mp = std::map<int, std::unique_ptr<MyClass>>{}]() mutable 
    { 
    mp.emplace(0, std::make_unique<MyClass>()); 
    mp.emplace(1, std::make_unique<MyClass>()); 
    return std::move(mp); 
    }(); 
    return mp; 
} 

int main() 
{ 
    auto& m = f(); 
} 
1

書寫定製crestion代碼看起來很無聊,並得到清晰的方式。

這裏是相當高效的通用容器初始化代碼。它像一個初始化器列表那樣將數據存儲在臨時std::array中,但是它不是將它們製作爲const

make_map需要偶數個元素的,第一個是密鑰的第二值。

template<class E, std::size_t N> 
struct make_container_t{ 
    std::array<E,N> elements; 
    template<class Container> 
    operator Container()&&{ 
    return { 
     std::make_move_iterator(begin(elements)), 
     std::make_move_iterator(end(elements)) 
    }; 
    } 
}; 
template<class E0, class...Es> 
make_container_t<E0, 1+sizeof...(Es)> 
make_container(E0 e0, Es... es){ 
    return {{{std::move(e0), std::move(es)...}}}; 
} 

namespace details{ 
    template<std::size_t...Is, class K0, class V0, class...Ts> 
    make_container_t<std::pair<K0,V0>,sizeof...(Is)> 
    make_map(std::index_sequence<Is...>, std::tuple<K0&,V0&,Ts&...> ts){ 
    return {{{ 
     std::make_pair(
     std::move(std::get<Is*2>(ts)), 
     std::move(std::get<Is*2+1>(ts))) 
    )... 
    }}}; 
    } 
} 
template<class...Es> 
auto make_map(Es... es){ 
    ststic_assert(!(sizeof...(es)&1), "key missing a value? Try even arguments."); 
    return details::make_map(
    std::make_index_sequence<sizeof...(Es)/2>{}, 
    std::tie(es...) 
); 
} 

這應該將其降低到:

static std::map<int, std::unique_ptr<MyClass>> = 
    make_map(0, std::make_unique<MyClass>()); 

...禁止錯別字。