如何调整 std::vector<std::queue<std::unique_ptr<int>>> 的大小?

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

我正在尝试执行以下操作:

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

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

但我在 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,
    |                                                                        ^~~~~

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.

这似乎适用于 libc++,例如clang++ test.cpp -o test -stdlib=libc++.

这是一个已知的 libstdc++ 错误吗?知道如何解决这个问题吗?

这不是错误。 std::queue<std::unique_ptr<int>> 的移动构造函数在 libc++ 中是 noexcept(true),但在 libstdc++ 中不是。因此,对于 libstdc++,std::vector 必须使用复制构造函数进行重新分配。但是它的调用会产生一个编译器错误,因为自然地,一个唯一指针队列可能不会被复制。

证明:https://godbolt.org/z/9j9P59

请注意,这不是错误,因为标准并未规定 std::queue 的移动构造函数为 noexcept。但是,实现是 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.

这就是 libc++ 在这里所做的:https://github.com/llvm/llvm-project/blob/master/libcxx/include/queue#L241

如果您通读 libstdc++ 的源代码,在 vector 的调整大小中,它最终会调用,

std::__uninitialized_move_if_noexcept_a

从旧内存 space 移动或复制到新内存 space 的功能。

这个函数做的是如果移动构造函数没有抛出则移动底层对象,否则复制它。

如果您通读 std::queue 的 libstdc++ 实现,

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

后面显然没有noexcept,意思就是可能会抛出。因此libstdc++会在resize时调用queue的copy constructor,std::unique_ptr无法copy,编译错误

坦率地说,这应该在 c++11 之后工作,即使它不是错误而是 c++11 之前时代的旧行为。