Search code examples
c++gcclibstdc++compiler-bug

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


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?


Solution

  • 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.