PDA

View Full Version : [C++] Uniform initialization syntax per mappa con unique_ptr


biowep
15-11-2014, 16:37
Salve, volevo gentilmente sapere per quale motivo il seguente codice non è corretto nel caso in cui si usi uno smart pointer con la sintassi di inizializzazione uniforme. Se la mappa contenesse un puntatore normale, allora funzionerebbe anche con la nuova sintassi.


#include <map>
#include <memory>
using namespace std;
int main() {
map<string, unique_ptr<Test> > a;

a.insert({ //non funziona
string("a"),
unique_ptr<Test>(new Test())
});

a.insert(make_pair( //funziona
string("a"),
unique_ptr<Test>(new Test())
));

return 0;
}

Mi serve per initializzare in modo statico (e leggibile) la mappa in questo modo (è un esempio):
map<string, unique_ptr<Test> > a = {
{"a", new Test()},
{"b", new Test()},
{...}
}

L'errore, si verifica in qualche libreria ed io non riesco a capire, quindi copio tutto:

16:41:43 **** Incremental Build of configuration Debug for project test ****
make all
Building file: ../src/test.cpp
Invoking: Cross G++ Compiler
g++ -D__cplusplus=201103L -O0 -g3 -Wall -c -fmessage-length=0 -std=c++11 -MMD -MP -MF"src/test.d" -MT"src/test.d" -o "src/test.o" "../src/test.cpp"
In file included from c:/MinGW64/x86_64-w64-mingw32/include/c++/x86_64-w64-mingw32/bits/c++allocator.h:33:0,
from c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/allocator.h:46,
from c:/MinGW64/x86_64-w64-mingw32/include/c++/string:41,
from c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/locale_classes.h:40,
from c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/ios_base.h:41,
from c:/MinGW64/x86_64-w64-mingw32/include/c++/ios:42,
from c:/MinGW64/x86_64-w64-mingw32/include/c++/ostream:38,
from c:/MinGW64/x86_64-w64-mingw32/include/c++/iostream:39,
from ../src/test.cpp:9:
c:/MinGW64/x86_64-w64-mingw32/include/c++/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::pair<const std::basic_string<char>, std::unique_ptr<Test> >; _Args = {const std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::unique_ptr<Test, std::default_delete<Test> > >&}; _Tp = std::_Rb_tree_node<std::pair<const std::basic_string<char>, std::unique_ptr<Test> > >]':
c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/alloc_traits.h:253:4: required from 'static std::_Require<typename std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::type> std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::pair<const std::basic_string<char>, std::unique_ptr<Test> >; _Args = {const std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::unique_ptr<Test, std::default_delete<Test> > >&}; _Alloc = std::allocator<std::_Rb_tree_node<std::pair<const std::basic_string<char>, std::unique_ptr<Test> > > >; std::_Require<typename std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::type> = void]'
c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/alloc_traits.h:399:57: required from 'static decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = std::pair<const std::basic_string<char>, std::unique_ptr<Test> >; _Args = {const std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::unique_ptr<Test, std::default_delete<Test> > >&}; _Alloc = std::allocator<std::_Rb_tree_node<std::pair<const std::basic_string<char>, std::unique_ptr<Test> > > >; decltype (_S_construct(__a, __p, (forward<_Args>)(std::allocator_traits::construct::__args)...)) = <type error>]'
c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/stl_tree.h:423:42: required from 'std::_Rb_tree_node<_Val>* std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_create_node(_Args&& ...) [with _Args = {const std::pair<const std::basic_string<char, std::char_traits<char>, std::allocator<char> >, std::unique_ptr<Test, std::default_delete<Test> > >&}; _Key = std::basic_string<char>; _Val = std::pair<const std::basic_string<char>, std::unique_ptr<Test> >; _KeyOfValue = std::_Select1st<std::pair<const std::basic_string<char>, std::unique_ptr<Test> > >; _Compare = std::less<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, std::unique_ptr<Test> > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Link_type = std::_Rb_tree_node<std::pair<const std::basic_string<char>, std::unique_ptr<Test> > >*]'
c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/stl_tree.h:1143:66: required from 'std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_(std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Base_ptr, std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Base_ptr, _Arg&&) [with _Arg = const std::pair<const std::basic_string<char>, std::unique_ptr<Test> >&; _Key = std::basic_string<char>; _Val = std::pair<const std::basic_string<char>, std::unique_ptr<Test> >; _KeyOfValue = std::_Select1st<std::pair<const std::basic_string<char>, std::unique_ptr<Test> > >; _Compare = std::less<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, std::unique_ptr<Test> > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::iterator = std::_Rb_tree_iterator<std::pair<const std::basic_string<char>, std::unique_ptr<Test> > >; std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_Base_ptr = std::_Rb_tree_node_base*]'
c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/stl_tree.h:1502:38: required from 'std::pair<std::_Rb_tree_iterator<_Val>, bool> std::_Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::_M_insert_unique(_Arg&&) [with _Arg = const std::pair<const std::basic_string<char>, std::unique_ptr<Test> >&; _Key = std::basic_string<char>; _Val = std::pair<const std::basic_string<char>, std::unique_ptr<Test> >; _KeyOfValue = std::_Select1st<std::pair<const std::basic_string<char>, std::unique_ptr<Test> > >; _Compare = std::less<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, std::unique_ptr<Test> > >]'
c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/stl_map.h:627:37: required from 'std::pair<typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<std::pair<const _Key, _Tp> >::other>::iterator, bool> std::map<_Key, _Tp, _Compare, _Alloc>::insert(const value_type&) [with _Key = std::basic_string<char>; _Tp = std::unique_ptr<Test>; _Compare = std::less<std::basic_string<char> >; _Alloc = std::allocator<std::pair<const std::basic_string<char>, std::unique_ptr<Test> > >; typename std::_Rb_tree<_Key, std::pair<const _Key, _Tp>, std::_Select1st<std::pair<const _Key, _Tp> >, _Compare, typename __gnu_cxx::__alloc_traits<_Alloc>::rebind<std::pair<const _Key, _Tp> >::other>::iterator = std::_Rb_tree_iterator<std::pair<const std::basic_string<char>, std::unique_ptr<Test> > >; std::map<_Key, _Tp, _Compare, _Alloc>::value_type = std::pair<const std::basic_string<char>, std::unique_ptr<Test> >]'
../src/test.cpp:42:3: required from here
c:/MinGW64/x86_64-w64-mingw32/include/c++/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 std::basic_string<char>; _T2 = std::unique_ptr<Test>]'
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^
In file included from c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/stl_algobase.h:64:0,
from c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/char_traits.h:39,
from c:/MinGW64/x86_64-w64-mingw32/include/c++/ios:40,
from c:/MinGW64/x86_64-w64-mingw32/include/c++/ostream:38,
from c:/MinGW64/x86_64-w64-mingw32/include/c++/iostream:39,
from ../src/test.cpp:9:
c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/stl_pair.h:127:17: note: 'constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const std::basic_string<char>; _T2 = std::unique_ptr<Test>]' is implicitly deleted because the default definition would be ill-formed:
constexpr pair(const pair&) = default;
^
c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/stl_pair.h:127:17: error: use of deleted function 'std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = Test; _Dp = std::default_delete<Test>]'
In file included from c:/MinGW64/x86_64-w64-mingw32/include/c++/memory:81:0,
from ../src/test.cpp:13:
c:/MinGW64/x86_64-w64-mingw32/include/c++/bits/unique_ptr.h:356:7: note: declared here
unique_ptr(const unique_ptr&) = delete;
^
make: *** [src/test.o] Error 1

vendettaaaaa
15-11-2014, 16:47
Mi pare sia dovuto al fatto che std::unique_ptr<T> non è copiabile ma solo movable, e con la prima sintassi crei un unique_ptr che viene passato ad insert per const reference, causando la chiamata al copy constructor. make_pair invece crea un std::pair senza creare un unique_ptr temporaneo, e quindi non c'è copia.

WarDuck
15-11-2014, 17:54
Mi pare sia dovuto al fatto che std::unique_ptr<T> non è copiabile ma solo movable, e con la prima sintassi crei un unique_ptr che viene passato ad insert per const reference, causando la chiamata al copy constructor. make_pair invece crea un std::pair senza creare un unique_ptr temporaneo, e quindi non c'è copia.

This!

Unique Ptr è studiato per evitare la copia (altrimenti non sarebbe più un unique pointer).

Al posto di fare insert puoi provare emplace:


...

map<string, unique_ptr<int>> a;

a.emplace("a", unique_ptr<int>(new int(4)));

...


Oppure in C++14:


...

map<string, unique_ptr<int>> a;

a.emplace("a", make_unique<int>(4));

...

biowep
15-11-2014, 18:26
Ma se, da quel che leggo nella documentazione, map::emplace prende degli argomenti e costruisce gli oggetti direttamente, perché allora non posso fare:

map<string, unique_ptr<int>> a;
a.emplace("a", new int(4));

Dovrebbe prendere il puntatore ad intero restituito dall'istruzione new int(4) e costruire un oggetto unique_ptr<int>

WarDuck
15-11-2014, 18:45
Ma se, da quel che leggo nella documentazione, map::emplace prende degli argomenti e costruisce gli oggetti direttamente, perché allora non posso fare:

map<string, unique_ptr<int>> a;
a.emplace("a", new int(4));

Dovrebbe prendere il puntatore ad intero restituito dall'istruzione new int(4) e costruire un oggetto unique_ptr<int>

Ho provato e vedendo l'errore sembra che lui cerchi di costruire il pair a partire da due reference.


/usr/include/c++/4.9.2/bits/stl_pair.h:112:26: note: no known conversion for argument 2 from ‘int*’ to ‘const std::unique_ptr<int>&’
/usr/include/c++/4.9.2/bits/stl_pair.h:108:26: note: constexpr std::pair<_T1, _T2>::pair() [with _T1 = const std::basic_string<char>; _T2 = std::unique_ptr<int>]


Un reference ad int* non può essere convertito in un reference a unique_ptr<int>, quindi fallisce.

La costruzione di un oggetto a partire dal solo parametro del costruttore può funzionare solo se lì viene richiesto un oggetto e non un suo reference.

biowep
15-11-2014, 18:52
Quindi sarebbe una mancanza della classe unique_ptr?

Grazie delle risposte, credo che d'ora in poi userò emplace invece che insert, sembra più efficiente da quel che leggo.

WarDuck
16-11-2014, 13:35
Quindi sarebbe una mancanza della classe unique_ptr?

Grazie delle risposte, credo che d'ora in poi userò emplace invece che insert, sembra più efficiente da quel che leggo.

No, dipende dal costruttore di pair, che vuole necessariamente 2 reference.

http://www.cplusplus.com/reference/utility/pair/pair/

In soldoni lui si aspetta che gli oggetti siano già stati costruiti precedentemente.

La costruzione dell'oggetto "implicita" avrebbe funzionato se il prototipo fosse stato:

pair(T a, T b);