- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我正在尝试编译一些代码,它简化为:
#include <memory>
#include <vector>
#include <QString>
class Category
{
std::vector<std::unique_ptr<int>> data;
QString name;
};
int main()
{
std::vector<Category> categories;
categories.emplace_back();
};
In file included from /opt/gcc-4.8/include/c++/4.8.2/memory:64:0,
from test.cpp:1:
/opt/gcc-4.8/include/c++/4.8.2/bits/stl_construct.h: In instantiation of ‘void std::_Construct(_T1*, _Args&& ...) [with _T1 = std::unique_ptr<int>; _Args = {const std::unique_ptr<int, std::default_delete<int> >&}]’:
/opt/gcc-4.8/include/c++/4.8.2/bits/stl_uninitialized.h:75:53: required from ‘static _ForwardIterator std::__uninitialized_copy<_TrivialValueTypes>::__uninit_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*; bool _TrivialValueTypes = false]’
/opt/gcc-4.8/include/c++/4.8.2/bits/stl_uninitialized.h:117:41: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*]’
/opt/gcc-4.8/include/c++/4.8.2/bits/stl_uninitialized.h:258:63: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = __gnu_cxx::__normal_iterator<const std::unique_ptr<int>*, std::vector<std::unique_ptr<int> > >; _ForwardIterator = std::unique_ptr<int>*; _Tp = std::unique_ptr<int>]’
/opt/gcc-4.8/include/c++/4.8.2/bits/stl_vector.h:316:32: required from ‘std::vector<_Tp, _Alloc>::vector(const std::vector<_Tp, _Alloc>&) [with _Tp = std::unique_ptr<int>; _Alloc = std::allocator<std::unique_ptr<int> >]’
test.cpp:5:7: [ skipping 2 instantiation contexts, use -ftemplate-backtrace-limit=0 to disable ]
/opt/gcc-4.8/include/c++/4.8.2/bits/stl_uninitialized.h:117:41: required from ‘_ForwardIterator std::uninitialized_copy(_InputIterator, _InputIterator, _ForwardIterator) [with _InputIterator = Category*; _ForwardIterator = Category*]’
/opt/gcc-4.8/include/c++/4.8.2/bits/stl_uninitialized.h:258:63: required from ‘_ForwardIterator std::__uninitialized_copy_a(_InputIterator, _InputIterator, _ForwardIterator, std::allocator<_Tp>&) [with _InputIterator = Category*; _ForwardIterator = Category*; _Tp = Category]’
/opt/gcc-4.8/include/c++/4.8.2/bits/stl_uninitialized.h:281:69: required from ‘_ForwardIterator std::__uninitialized_move_if_noexcept_a(_InputIterator, _InputIterator, _ForwardIterator, _Allocator&) [with _InputIterator = Category*; _ForwardIterator = Category*; _Allocator = std::allocator<Category>]’
/opt/gcc-4.8/include/c++/4.8.2/bits/vector.tcc:415:43: required from ‘void std::vector<_Tp, _Alloc>::_M_emplace_back_aux(_Args&& ...) [with _Args = {}; _Tp = Category; _Alloc = std::allocator<Category>]’
/opt/gcc-4.8/include/c++/4.8.2/bits/vector.tcc:101:54: required from ‘void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {}; _Tp = Category; _Alloc = std::allocator<Category>]’
test.cpp:14:29: required from here
/opt/gcc-4.8/include/c++/4.8.2/bits/stl_construct.h:75:7: 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>]’
{ ::new(static_cast<void*>(__p)) _T1(std::forward<_Args>(__args)...); }
^
In file included from /opt/gcc-4.8/include/c++/4.8.2/memory:81:0,
from test.cpp:1:
/opt/gcc-4.8/include/c++/4.8.2/bits/unique_ptr.h:273:7: error: declared here
unique_ptr(const unique_ptr&) = delete;
^
name
成员(member)来自 Category
,它编译得很好。 data
只是一个unique_ptr<int>
它编译得很好,而不是指针 vector 。 Category
在 main()
而不是创建一个 vector 并做 emplace_back()
,它编译得很好。 QString
与 std::string
,它编译得很好。 最佳答案
这里的关键问题是 std::vector
试图为尽可能多的操作提供 strong exception safety guarantee,但是,为了做到这一点,它需要元素类型的支持。对于 push_back
、 emplace_back
和 friend ,主要问题是如果需要重新分配会发生什么,因为需要将现有元素复制/移动到新存储。
相关标准措辞在[23.3.6.5p1]:
Remarks: Causes reallocation if the new size is greater than the old capacity. If no reallocation happens, all the iterators and references before the insertion point remain valid. If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of
T
or by anyInputIterator
operation there are no effects. If an exception is thrown while inserting a single element at the end andT
isCopyInsertable
oris_nothrow_move_constructible<T>::value
istrue
, there are no effects. Otherwise, if an exception is thrown by the move constructor of a non-CopyInsertable
T
, the effects are unspecified.
is_nothrow_move_constructible<T>::value == true
并不一定意味着
T
具有
noexcept
移动构造函数;
noexcept
复制构造函数采用
const T&
也可以。
vector
实现通常会尝试为以下解决方案之一生成代码,以按优先级降序将现有元素复制/移动到新存储(
T
是元素类型,我们在这里对类类型感兴趣):
T
有一个可用的(存在的、没有删除的、没有歧义的、可访问的等)noexcept
移动构造函数,使用它;在新存储中构造元素时不能抛出异常,因此无需恢复到以前的状态。 T
有一个可用的复制构造函数,无论 noexcept
与否,都需要一个 const T&
,使用它;即使复制引发异常,我们也可以恢复到以前的状态,因为原始文件仍然存在,未经修改。 T
有一个可用的移动构造函数,可能会抛出异常,使用它;但是,不能再提供强大的异常安全保证。 std::move_if_noexcept
或类似的东西来实现。
Category
在构造函数方面提供了什么。 None 是显式声明的,因此隐式声明了默认构造函数、复制构造函数和移动构造函数。
data
是一个 std::vector
,而 vector
的拷贝构造函数不能是 noexcept
(它一般需要分配新的内存),所以不管 Category
有什么, noexcept
的拷贝构造函数都不能是 QString
。 std::vector<std::unique_ptr<int>>
的拷贝构造函数的定义调用了 std::unique_ptr<int>
的拷贝构造函数,显式删除,但这只会影响定义,只有在需要时才实例化。重载解析只需要声明,所以 Category
有一个隐式声明的复制构造函数,如果调用它会导致编译错误。 std::vector
有一个 noexcept
移动构造函数(见下面的注释),所以 data
不是问题。 QString
的旧版本(Qt 5.2 之前):Category
隐式声明的移动构造函数的定义将使用 QString
的复制构造函数,它采用 const QString&
,它可以绑定(bind)到右值(使用重载决议选择子对象的构造函数)。 QString
的复制构造函数没有指定为 noexcept
,因此 Category
的移动构造函数也不能是 noexcept
。 QString
有一个显式声明的移动构造函数,它将被 Category
的移动构造函数使用。但是,在 Qt 5.5 之前, QString
的移动构造函数不是 noexcept
,因此 Category
的移动构造函数也不能是 noexcept
。 QString
的移动构造函数被指定为 noexcept
,所以 Category
的移动构造函数也是 noexcept
。 Category
在所有情况下都有一个移动构造函数,但它可能不会移动
name
,也可能不是
noexcept
。
categories.emplace_back()
在使用 Qt 4 时(OP 的情况)不会生成使用
Category
的移动构造函数的代码,因为它不是
noexcept
。 (当然,在这种情况下没有要移动的现有元素,但这是运行时决定;
emplace_back
必须包含处理一般情况的代码路径,并且该代码路径必须编译。)因此,生成的代码调用
Category
的复制构造函数,这会导致编译错误。
Category
提供一个移动构造函数并将其标记为
noexcept
(否则它不会有帮助)。
QString
无论如何都使用写时复制,因此在复制时不太可能抛出。
class Category
{
std::vector<std::unique_ptr<int>> data;
QString name;
public:
Category() = default;
Category(const Category&) = default;
Category(Category&& c) noexcept : data(std::move(c.data)), name(std::move(c.name)) { }
// assignment operators
};
QString
的移动构造函数,否则使用复制构造函数(就像隐式声明的移动构造函数一样)。现在构造函数是用户声明的,赋值运算符也必须考虑在内。
data
只是一个
unique_ptr<int>
)更有趣:
unique_ptr
有一个删除的复制构造函数;这会导致 Category
隐式声明的复制构造函数也被定义为已删除。 Category
的移动构造函数仍然如上声明(在 OP 的情况下不是 noexcept
)。 emplace_back
生成的代码不能使用 Category
的复制构造函数,所以它必须使用移动构造函数,即使它可以抛出(参见上面的第一部分)。代码可以编译,但不再提供强大的异常安全保证。 vector
的移动构造函数最近才在标准中指定为
noexcept
,在 C++14 之后,因为在工作草案中采用了
N4258。然而在实践中,libstdc++ 和 libc++ 从 C++0x 时代就已经为
noexcept
提供了一个
vector
移动构造函数;与标准的规范相比,允许实现加强异常规范,所以没关系。
noexcept(is_nothrow_move_constructible<allocator_type>::value)
用于 C++14 及更低版本,但自 C++11([17.6.3.5] 中的表 28)以来,分配器必须不能移动和复制构造,因此这对于符合标准的分配器来说是多余的。
关于c++ - 为什么 QString 和 vector<unique_ptr<int>> 在这里显得不兼容?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34142490/
我是一名优秀的程序员,十分优秀!