You are not logged in.

#1 2019-02-20 17:55:04

snack
Member
From: Italy
Registered: 2009-01-13
Posts: 753

Error when adding non-copyable element to std::unordered_map

I get a strange error when compiling my code with gcc 6 (from AUR) but not with gcc 8 (from core repo). I have a struct containing a std::unique_ptr, whick makes it non-copyable. I put this struct inside a std::unordered map as follows:

#include <memory>
#include <unordered_map>

struct test{
        std::unique_ptr<int> i;
};

int main(){
        std::unordered_map<int, test> m;
        m.insert({0, test()});
}

The argument of insert is a list from which a std::pair<int,test> is implicitly constructed (I think). The above code do not produce any error with gcc 8:

$ c++ reproducer.cpp 
$

but with gcc  6 it ends with:

$ c++-6 reproducer.cpp 
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/x86_64-pc-linux-gnu/bits/c++allocator.h:33:0,
                 from /usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/bits/allocator.h:46,
                 from /usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/memory:63,
                 from reproducer.cpp:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/ext/new_allocator.h: In instantiation of ‘void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = std::pair<const int, test>; _Args = {const std::pair<const int, test>&}; _Tp = std::pair<const int, test>]’:
/usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/bits/alloc_traits.h:475:4:   required from ‘static void std::allocator_traits<std::allocator<_Tp1> >::construct(std::allocator_traits<std::allocator<_Tp1> >::allocator_type&, _Up*, _Args&& ...) [with _Up = std::pair<const int, test>; _Args = {const std::pair<const int, test>&}; _Tp = std::pair<const int, test>; std::allocator_traits<std::allocator<_Tp1> >::allocator_type = std::allocator<std::pair<const int, test> >]’
/usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/bits/hashtable_policy.h:1953:37:   required from ‘std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type* std::__detail::_Hashtable_alloc<_NodeAlloc>::_M_allocate_node(_Args&& ...) [with _Args = {const std::pair<const int, test>&}; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const int, test>, false> >; std::__detail::_Hashtable_alloc<_NodeAlloc>::__node_type = std::__detail::_Hash_node<std::pair<const int, test>, false>]’
/usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/bits/hashtable_policy.h:180:58:   required from ‘std::__detail::_AllocNode<_NodeAlloc>::__node_type* std::__detail::_AllocNode<_NodeAlloc>::operator()(_Arg&&) const [with _Arg = const std::pair<const int, test>&; _NodeAlloc = std::allocator<std::__detail::_Hash_node<std::pair<const int, test>, false> >; std::__detail::_AllocNode<_NodeAlloc>::__node_type = std::__detail::_Hash_node<std::pair<const int, test>, false>]’
/usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/bits/hashtable.h:1681:18:   required from ‘std::pair<typename std::__detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, _Hash, _Traits>::iterator, bool> std::_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::_M_insert(_Arg&&, const _NodeGenerator&, std::true_type) [with _Arg = const std::pair<const int, test>&; _NodeGenerator = std::__detail::_AllocNode<std::allocator<std::__detail::_Hash_node<std::pair<const int, test>, false> > >; _Key = int; _Value = std::pair<const int, test>; _Alloc = std::allocator<std::pair<const int, test> >; _ExtractKey = std::__detail::_Select1st; _Equal = std::equal_to<int>; _H1 = std::hash<int>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; typename std::__detail::_Hashtable_base<_Key, _Value, _ExtractKey, _Equal, _H1, _H2, _Hash, _Traits>::iterator = std::__detail::_Node_iterator<std::pair<const int, test>, false, false>; std::true_type = std::integral_constant<bool, true>]’
/usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/bits/hashtable_policy.h:713:55:   required from ‘std::__detail::_Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__ireturn_type std::__detail::_Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::insert(const value_type&) [with _Key = int; _Value = std::pair<const int, test>; _Alloc = std::allocator<std::pair<const int, test> >; _ExtractKey = std::__detail::_Select1st; _Equal = std::equal_to<int>; _H1 = std::hash<int>; _H2 = std::__detail::_Mod_range_hashing; _Hash = std::__detail::_Default_ranged_hash; _RehashPolicy = std::__detail::_Prime_rehash_policy; _Traits = std::__detail::_Hashtable_traits<false, false, true>; std::__detail::_Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::__ireturn_type = std::pair<std::__detail::_Node_iterator<std::pair<const int, test>, false, false>, bool>; std::__detail::_Insert_base<_Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits>::value_type = std::pair<const int, test>]’
/usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/bits/unordered_map.h:550:31:   required from ‘std::pair<typename std::_Hashtable<_Key, std::pair<const _Key, _Tp>, _Alloc, std::__detail::_Select1st, _Pred, _Hash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<std::__not_<std::__and_<std::__is_fast_hash<_Hash>, std::__detail::__is_noexcept_hash<_Key, _Hash> > >::value, false, true> >::iterator, bool> std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::insert(const value_type&) [with _Key = int; _Tp = test; _Hash = std::hash<int>; _Pred = std::equal_to<int>; _Alloc = std::allocator<std::pair<const int, test> >; typename std::_Hashtable<_Key, std::pair<const _Key, _Tp>, _Alloc, std::__detail::_Select1st, _Pred, _Hash, std::__detail::_Mod_range_hashing, std::__detail::_Default_ranged_hash, std::__detail::_Prime_rehash_policy, std::__detail::_Hashtable_traits<std::__not_<std::__and_<std::__is_fast_hash<_Hash>, std::__detail::__is_noexcept_hash<_Key, _Hash> > >::value, false, true> >::iterator = std::__detail::_Node_iterator<std::pair<const int, test>, false, false>; std::unordered_map<_Key, _Tp, _Hash, _Pred, _Alloc>::value_type = std::pair<const int, test>]’
reproducer.cpp:10:22:   required from here
/usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/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 int; _T2 = test]’
  { ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
    ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/bits/stl_algobase.h:64:0,
                 from /usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/memory:62,
                 from reproducer.cpp:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/bits/stl_pair.h:299:17: note: ‘constexpr std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2>&) [with _T1 = const int; _T2 = test]’ is implicitly deleted because the default definition would be ill-formed:
       constexpr pair(const pair&) = default;
                 ^~~~
/usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/bits/stl_pair.h:299:17: error: use of deleted function ‘test::test(const test&)’
reproducer.cpp:4:8: note: ‘test::test(const test&)’ is implicitly deleted because the default definition would be ill-formed:
 struct test{
        ^~~~
reproducer.cpp:4:8: error: use of deleted function ‘std::unique_ptr<_Tp, _Dp>::unique_ptr(const std::unique_ptr<_Tp, _Dp>&) [with _Tp = int; _Dp = std::default_delete<int>]’
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/memory:81:0,
                 from reproducer.cpp:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/6.5.0/include/c++/bits/unique_ptr.h:361:7: note: declared here
       unique_ptr(const unique_ptr&) = delete;
       ^~~~~~~~~~

The error can be fixed by explicitly constructing the std::pair that is the argument of insert:

...
  m.insert(std::pair<int, test>{0, test()});
...
$ c++-6 reproducer.cpp 
$ 

so I guess that the problem is in passing an initializer list to insert. I do not understand why the two compilers behave differently since they both use the same dialect and I am not aware of any major difference between them regarding the handling of initializer list. I also wonder what the correct compiler behaviour should be according to the standard.
Thanks for any help.

Offline

Board footer

Powered by FluxBB