How to resize a std::vector<std::queue<std::unique_ptr<int>>>?

Viewed 314

I'm trying to do the following:

#include <memory>
#include <vector>
#include <queue>

int main() {
    std::vector<std::queue<std::unique_ptr<int>>> v;
    v.resize(10);
}

But I'm getting this with GCC 10.2:

$ g++ test.cpp -o test
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/memory:66,
                from test.cpp:1:
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h: In instantiation of '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = std::_Deque_iterator<std::unique_ptr<int>, const std::unique_ptr<int>&, const std::unique_ptr<int>*>; _ForwardIterator = std::_Deque_iterator<std::unique_ptr<int>, std::unique_ptr<int>&, std::unique_ptr<int>*>]':
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:325:37:   required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = std::_Deque_iterator<std::unique_ptr<int>, const std::unique_ptr<int>&, const std::unique_ptr<int>*>; _ForwardIterator = std::_Deque_iterator<std::unique_ptr<int>, std::unique_ptr<int>&, std::unique_ptr<int>*>; _Tp = std::unique_ptr<int>]'
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_deque.h:896:36:   required from 'std::deque<_Tp, _Alloc>::deque(const std::deque<_Tp, _Alloc>&) [with _Tp = std::unique_ptr<int>; _Alloc = std::allocator<std::unique_ptr<int> >]'
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_queue.h:96:11:   required from 'void std::_Construct(_Tp*, _Args&& ...) [with _Tp = std::queue<std::unique_ptr<int> >; _Args = {const std::queue<std::unique_ptr<int, std::default_delete<int> >, std::deque<std::unique_ptr<int, std::default_delete<int> >, std::allocator<std::unique_ptr<int, std::default_delete<int> > > > >&}]'
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:91:18:   required from 'static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const std::queue<std::unique_ptr<int> >*; _ForwardIterator = std::queue<std::unique_ptr<int> >*; bool _TrivialValueTypes = false]'
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:150:15:   required from '_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = const std::queue<std::unique_ptr<int> >*; _ForwardIterator = std::queue<std::unique_ptr<int> >*]'
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:325:37:   required from '_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = const std::queue<std::unique_ptr<int> >*; _ForwardIterator = std::queue<std::unique_ptr<int> >*; _Tp = std::queue<std::unique_ptr<int> >]'
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:347:2:   required from '_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = std::queue<std::unique_ptr<int> >*; _ForwardIterator = std::queue<std::unique_ptr<int> >*; _Allocator = std::allocator<std::queue<std::unique_ptr<int> > >]'
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/vector.tcc:659:48:   required from 'void std::vector<_Tp, _Alloc>::_M_default_append(std::vector<_Tp, _Alloc>::size_type) [with _Tp = std::queue<std::unique_ptr<int> >; _Alloc = std::allocator<std::queue<std::unique_ptr<int> > >; std::vector<_Tp, _Alloc>::size_type = long unsigned int]'
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_vector.h:940:4:   required from 'void std::vector<_Tp, _Alloc>::resize(std::vector<_Tp, _Alloc>::size_type) [with _Tp = std::queue<std::unique_ptr<int> >; _Alloc = std::allocator<std::queue<std::unique_ptr<int> > >; std::vector<_Tp, _Alloc>::size_type = long unsigned int]'
test.cpp:7:16:   required from here
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:137:72: error: static assertion failed: result type must be constructible from value type of input range
137 |       static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
    |                                                                        ^~~~~

Same with clang++11:

$ clang++-11 test.cpp -o test
In file included from test.cpp:1:
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/memory:66:
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:137:7: error: static_assert failed due to requirement 'is_constructible<std::unique_ptr<int, std::default_delete<int>>, const std::unique_ptr<int, std::default_delete<int>> &>::value' "result type must be constructible from value type of input range"
    static_assert(is_constructible<_ValueType2, decltype(*__first)>::value,
    ^             ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:325:19: note: in instantiation of function template specialization 'std::uninitialized_copy<std::_Deque_iterator<std::unique_ptr<int, std::default_delete<int>>, const std::unique_ptr<int, std::default_delete<int>> &, const std::unique_ptr<int, std::default_delete<int>> *>, std::_Deque_iterator<std::unique_ptr<int, std::default_delete<int>>, std::unique_ptr<int, std::default_delete<int>> &, std::unique_ptr<int, std::default_delete<int>> *>>' requested here
    { return std::uninitialized_copy(__first, __last, __result); }
                ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_deque.h:896:14: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<std::_Deque_iterator<std::unique_ptr<int, std::default_delete<int>>, const std::unique_ptr<int, std::default_delete<int>> &, const std::unique_ptr<int, std::default_delete<int>> *>, std::_Deque_iterator<std::unique_ptr<int, std::default_delete<int>>, std::unique_ptr<int, std::default_delete<int>> &, std::unique_ptr<int, std::default_delete<int>> *>, std::unique_ptr<int, std::default_delete<int>>>' requested here
    { std::__uninitialized_copy_a(__x.begin(), __x.end(),
            ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_queue.h:96:11: note: in instantiation of member function 'std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>::deque' requested here
    class queue
        ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:91:8: note: in instantiation of function template specialization 'std::_Construct<std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>>, const std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> &>' requested here
                std::_Construct(std::__addressof(*__cur), *__first);
                    ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:150:2: note: in instantiation of function template specialization 'std::__uninitialized_copy<false>::__uninit_copy<const std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *, std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *>' requested here
        __uninit_copy(__first, __last, __result);
        ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:325:19: note: in instantiation of function template specialization 'std::uninitialized_copy<const std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *, std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *>' requested here
    { return std::uninitialized_copy(__first, __last, __result); }
                ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:346:19: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<const std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *, std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *, std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>>>' requested here
    return std::__uninitialized_copy_a
                ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/vector.tcc:659:14: note: in instantiation of function template specialization 'std::__uninitialized_move_if_noexcept_a<std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *, std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *, std::allocator<std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>>>>' requested here
                    std::__uninitialized_move_if_noexcept_a(
                        ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_vector.h:940:4: note: in instantiation of member function 'std::vector<std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>>, std::allocator<std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>>>>::_M_default_append' requested here
        _M_default_append(__new_size - size());
        ^
test.cpp:7:7: note: in instantiation of member function 'std::vector<std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>>, std::allocator<std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>>>>::resize' requested here
    v.resize(10);
    ^
In file included from test.cpp:1:
In file included from /usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/memory:65:
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_construct.h:109:38: error: call to deleted constructor of 'std::unique_ptr<int, std::default_delete<int>>'
    { ::new(static_cast<void*>(__p)) _Tp(std::forward<_Args>(__args)...); }
                                    ^   ~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:91:8: note: in instantiation of function template specialization 'std::_Construct<std::unique_ptr<int, std::default_delete<int>>, const std::unique_ptr<int, std::default_delete<int>> &>' requested here
                std::_Construct(std::__addressof(*__cur), *__first);
                    ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:150:2: note: in instantiation of function template specialization 'std::__uninitialized_copy<false>::__uninit_copy<std::_Deque_iterator<std::unique_ptr<int, std::default_delete<int>>, const std::unique_ptr<int, std::default_delete<int>> &, const std::unique_ptr<int, std::default_delete<int>> *>, std::_Deque_iterator<std::unique_ptr<int, std::default_delete<int>>, std::unique_ptr<int, std::default_delete<int>> &, std::unique_ptr<int, std::default_delete<int>> *>>' requested here
        __uninit_copy(__first, __last, __result);
        ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:325:19: note: in instantiation of function template specialization 'std::uninitialized_copy<std::_Deque_iterator<std::unique_ptr<int, std::default_delete<int>>, const std::unique_ptr<int, std::default_delete<int>> &, const std::unique_ptr<int, std::default_delete<int>> *>, std::_Deque_iterator<std::unique_ptr<int, std::default_delete<int>>, std::unique_ptr<int, std::default_delete<int>> &, std::unique_ptr<int, std::default_delete<int>> *>>' requested here
    { return std::uninitialized_copy(__first, __last, __result); }
                ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_deque.h:896:14: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<std::_Deque_iterator<std::unique_ptr<int, std::default_delete<int>>, const std::unique_ptr<int, std::default_delete<int>> &, const std::unique_ptr<int, std::default_delete<int>> *>, std::_Deque_iterator<std::unique_ptr<int, std::default_delete<int>>, std::unique_ptr<int, std::default_delete<int>> &, std::unique_ptr<int, std::default_delete<int>> *>, std::unique_ptr<int, std::default_delete<int>>>' requested here
    { std::__uninitialized_copy_a(__x.begin(), __x.end(),
            ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_queue.h:96:11: note: in instantiation of member function 'std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>::deque' requested here
    class queue
        ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:91:8: note: (skipping 2 contexts in backtrace; use -ftemplate-backtrace-limit=0 to see all)
                std::_Construct(std::__addressof(*__cur), *__first);
                    ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:325:19: note: in instantiation of function template specialization 'std::uninitialized_copy<const std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *, std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *>' requested here
    { return std::uninitialized_copy(__first, __last, __result); }
                ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_uninitialized.h:346:19: note: in instantiation of function template specialization 'std::__uninitialized_copy_a<const std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *, std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *, std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>>>' requested here
    return std::__uninitialized_copy_a
                ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/vector.tcc:659:14: note: in instantiation of function template specialization 'std::__uninitialized_move_if_noexcept_a<std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *, std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>> *, std::allocator<std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>>>>' requested here
                    std::__uninitialized_move_if_noexcept_a(
                        ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/stl_vector.h:940:4: note: in instantiation of member function 'std::vector<std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>>, std::allocator<std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>>>>::_M_default_append' requested here
        _M_default_append(__new_size - size());
        ^
test.cpp:7:7: note: in instantiation of member function 'std::vector<std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>>, std::allocator<std::queue<std::unique_ptr<int, std::default_delete<int>>, std::deque<std::unique_ptr<int, std::default_delete<int>>, std::allocator<std::unique_ptr<int, std::default_delete<int>>>>>>>::resize' requested here
    v.resize(10);
    ^
/usr/lib/gcc/x86_64-pc-linux-gnu/10.2.0/include/g++-v10/bits/unique_ptr.h:468:7: note: 'unique_ptr' has been explicitly marked deleted here
    unique_ptr(const unique_ptr&) = delete;
    ^
2 errors generated.

This seems to work fine with libc++, e.g. clang++ test.cpp -o test -stdlib=libc++.

I this a known libstdc++ bug? Any idea on how to work around this?

2 Answers

It's not a bug. Move constructor of std::queue<std::unique_ptr<int>> is noexcept(true) in libc++, but not in libstdc++. Consequently, with libstdc++, std::vector must use copy constructor for reallocation. But its call generates a compiler error, since, naturally, a queue of unique pointers may not be copied.

Proof: https://godbolt.org/z/9j9P59

Note that it's not a bug because the Standard does not prescribe move constructor for std::queue to be noexcept. However, implementations are allowed to strengthen the exception specification:

An implementation may strengthen the exception specification for a non-virtual function by adding a non-throwing exception specification.

This is what libc++ does here: https://github.com/llvm/llvm-project/blob/master/libcxx/include/queue#L241.

If you read through libstdc++'s source code, in vector's resize, it finally calls,

std::__uninitialized_move_if_noexcept_a

function to move or copy from the old memory space to new memory space.

What this function does is to move the underlying objects if the move constructor does not throw, otherwise copy it.

If you read through the std::queue's implementation of libstdc++,

template<typename _Alloc, typename _Requires = _Uses<_Alloc>>
queue(queue&& __q, const _Alloc& __a)
   : c(std::move(__q.c), __a) { }

There is apparently no noexcept behind, meaning that it may throw. Thus libstdc++ will call queue's copy constructor upon resize, and std::unique_ptr can't be copied, thus there's compilation error.

Frankly this should work after c++11, even it is not a bug but rather an old behavior in pre-c++11 era.

Related