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

4

我想要做以下事情:

#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++ bug吗?有什么解决方法吗?

1
即使 v.reserve(10); 失败了,最好的方法是使用 std::vector<std::queue<std::unique_ptr<int>>> v(10); (这使得 std::vector 的意图变得有些无用)。 - Scheff's Cat
clang++错误('unique_ptr' has been explicitly marked deleted here: unique_ptr(const unique_ptr&) = delete;)表明resize()似乎使用了复制,而这显然会失败。我认为这是一个bug,因为只应该使用默认构造函数和移动构造函数,但我不是标准/编译器专家。 - stefaanv
2个回答

5
这不是一个 bug。在 libc++ 中,std::queue<std::unique_ptr<int>> 的移动构造函数是 noexcept(true),但在 libstdc++ 中不是。因此,在使用 libstdc++ 时,std::vector 必须使用复制构造函数进行重新分配。但是,由于独特指针队列不能被复制,所以其调用会生成编译器错误。

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

请注意,这不是一个 bug,因为标准没有规定 std::queue 的移动构造函数必须是 noexcept。然而,实现可以 加强异常规范

通过添加非抛出异常规范,实现可以加强非虚拟函数的异常规范。

这是libc++在此处的作用:https://github.com/llvm/llvm-project/blob/master/libcxx/include/queue#L241

0
如果您阅读过libstdc++的源代码,在vector的resize中,它最终调用:
std::__uninitialized_move_if_noexcept_a

函数用于将数据从旧的内存空间移动或复制到新的内存空间。

该函数的作用是在移动构造函数不抛出异常时移动底层对象,否则进行复制操作。

如果您阅读过libstdc++中std::queue的实现代码,

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

显然没有noexcept后面, 这意味着它可能会抛出异常。因此,在调整大小时,libstdc++将调用队列的复制构造函数,而std::unique_ptr无法复制,因此会出现编译错误。

坦白说,这在c++11之后应该能工作,即使这不是一个bug,而是在c++11之前的旧行为。


这也不是移动构造函数。移动构造函数不应该需要2个参数。 - Daniel Langr

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接