2016-07-18 20 views
3

Ich habe mich gefragt. Stellen Sie sich Folgendes vor:Können Sie einen STL-Container mit unique_ptr aus einer Initialisierungsliste initialisieren?

#include <iostream> 
#include <map> 
#include <memory> 

int main() { 
    std::map< int, std::unique_ptr<int> > m = 
    { { 1, std::unique_ptr<int>(new int(3)) } }; 

    return(0); 
} 

Dies ist C++ 11. Es scheitert mit einem langen Rieses von Fehlermeldungen auf GCC, darunter ein

/usr/include/c++/4.9/ext/new_allocator.h:120:4: error: use of deleted function ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = std::unique_ptr<int>]’ 
    { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); } 

Ist es möglich, überhaupt zu kompilieren zu tun, was ich versuche, hier zu tun? Ich stelle fest, dass mit shared_ptr es OK funktioniert. Ist es möglich, es mit unique_ptr zu tun? Wenn ja, was fehlt mir? Wenn nicht, warum nicht?

Antwort

4

No.

die Elemente eines initializer_list nicht geändert werden kann.

Die unique_ptr kann nicht verschoben werden (weil es const ist) und es kann nicht kopiert werden (weil es nur eine Bewegungstyp ist), so dass Sie abgespritzt werden.

+0

Ah, danke. Ich vermutete, dass so etwas passierte - "conexpr" schien ein Hinweis zu sein. –

+0

@ mike4ty4 Der wahre Anhaltspunkt dort, was Marshall sagte, ist 'Verwendung von gelöschten Funktion [...] Paar (const std :: Paar <_T1, _T2> &) [mit _T1 = const int; _T2 = std :: unique_ptr] '. Das heißt: das scheitert, weil es ein Kopierkonstruktor ist, der versucht, beide Elemente zu kopieren, aber natürlich ist 'std :: unique_ptr' nicht kopierbar. Der Verschiebungskonstruktor würde Ihnen den gleichen Fehler geben, wenn Sie dies aufgerufen hätten –

0

Sicher, kein Problem.

Zuerst wird eine intelligente unique_ptr Proxy, so können wir sie etwa in einem const Kontext erstellen und verschieben:

template<class T> 
struct il_up { 
    mutable std::unique_ptr<T> ptr; 
    template<class U, 
    std::enable_if_t< std::is_convertible<U*, T*>{}, int>* =nullptr 
    > 
    il_up(std::unique_ptr<U> o): ptr(std::move(o)) {} 

    operator std::unique_ptr<T>() const { 
    return std::move(ptr); 
    } 
}; 

Wir wollen dann dieses Geschäft in einem initializer_list. Auch wenn es const ist, kann es die unique_ptr hinausreichen.

Dann wird ein Container-Herstellung Proxy die temporäre Initialisiererliste zu speichern:

template<class T> 
struct make_container { 
    std::initializer_list<T> il; 
    make_container(std::initializer_list<T> l):il(l) {} 

    template<class C> 
    operator C()&&{ 
    return {il.begin(), il.end()}; 
    } 
}; 

und wir sind fertig:

std::vector<std::unique_ptr<int>> vec = make_container<il_up<int>>{ 
    std::make_unique<int>(1), std::make_unique<int>(2), 
    std::make_unique<int>(3), std::make_unique<int>(4) 
}; 

live example.