我有一段C ++代码,不确定该代码是否正确。考虑下面的代码。
#include <memory>
#include <vector>
#include <map>
using namespace std;
int main(int argc, char* argv[])
{
vector<map<int, unique_ptr<int>>> v;
v.resize(5);
return EXIT_SUCCESS;
}
GCC可以毫无问题地编译此代码。但是,英特尔编译器(版本19)会因错误而停止:
/usr/local/ [...] /include/c++/7.3.0/ext/new_allocator.h(136): error: function "std::pair<_T1, _T2>::pair(const std::pair<_T1, _T2> &) [with _T1=const int, _T2=std::unique_ptr<int, std::default_delete<int>>]" (declared at line 292 of "/usr/local/ [...] /include/c++/7.3.0/bits/stl_pair.h") cannot be referenced -- it is a deleted function
{ ::new((void *)__p) _Up(std::forward<_Args>(__args)...); }
^
detected during:
[...]
instantiation of "void std::vector<_Tp, _Alloc>::resize(std::vector<_Tp, _Alloc>::size_type={std::size_t={unsigned long}}) [with _Tp=std::map<int, std::unique_ptr<int, std::default_delete<int>>, std::less<int>, std::allocator<std::pair<const int, std::unique_ptr<int, std::default_delete<int>>>>>, _Alloc=std::allocator<std::map<int, std::unique_ptr<int, std::default_delete<int>>, std::less<int>, std::allocator<std::pair<const int, std::unique_ptr<int, std::default_delete<int>>>>>>]"
at line 10 of "program.cpp"
两个编译器都可以毫无问题地编译以下代码。
#include <memory>
#include <vector>
#include <map>
using namespace std;
int main(int argc, char* argv[])
{
vector<unique_ptr<int>> v;
v.resize(5);
return EXIT_SUCCESS;
}
第一个代码因Intel编译器而失败,因为它试图创建unique_ptr的副本,该副本仅定义了一个移动构造函数。但是,我不确定第一个程序是否为合法的C ++程序。
我想知道第一个代码是否错误,或者英特尔编译器中是否有错误。如果第一个代码错误,那么第二个代码为什么正确?还是第二个错误?
问题源自从以下后置条件std::vector<T>::resize
,[vector.capacity] :
备注:如果不是由非CopyInsertable 的move构造函数引发异常
T
,则没有任何效果。
也就是说,如果重定位失败,向量必须保持不变。重定位可能失败的原因之一是由于异常,特别是当用于将元素从旧存储转移到新存储的复制或移动构造函数引发异常时。
复制元素是否会以任何方式更改原始存储?无1。是否移动元素改变了原有存储?是。哪个操作更有效?移动。向量可以总是喜欢复制吗?不总是。
如果move构造函数可以引发异常,则不可能恢复旧存储的原始内容,因为尝试将已移位的元素移回旧块可能会再次失败。在这种情况下,矢量仅在移动构造函数保证不会引发异常的情况下,才使用移动构造函数将其元素从旧存储位置重新定位到新存储(或在复制构造函数为无法使用)。函数如何保证不会抛出异常?一个将由说明者注释,并由操作员noexcept
进行测试noexcept
。
使用icc测试以下代码:
std::map<int, std::unique_ptr<int>> m;
static_assert(noexcept(std::map<int, std::unique_ptr<int>>(std::move(m))), "!");
断言失败。这意味着,m
是不是nothrow- MoveConstructible。
标准要求它是noexcept
吗?[map.overview]:
// [map.cons], construct/copy/destroy:
map(const map& x);
map(map&& x);
std::map
是Move-和CopyConstructible。都不要求不引发异常。
但是,允许实现提供此保证{{citation needed}}}。您的代码使用以下定义:
map(map&&) = default;
是否需要隐式生成的move构造函数noexcept
?[except.spec]:
继承构造函数([class.inhctor])和隐式声明的特殊成员函数(Clause [special])具有异常规范。如果
f
是继承的构造函数或隐式声明的默认构造函数,复制构造函数,move构造函数,析构函数,复制赋值运算符或移动赋值运算符,则其隐式异常规范在且仅当异常规范允许时指定type-id 由的隐式定义直接调用的函数;如果它直接调用的任何函数允许所有异常,并且具有异常规范,则允许所有异常T
T
f
f
f
noexcept(true)
如果它直接调用的每个函数都不允许有异常。
在这一点上,很难说是否应该由icc move构造函数隐式生成noexcept
。无论哪种方式,std::map
都不需要它本身不是MoveConstructible,因此它更多是实现质量问题(库的实现或构造函数的隐式生成的实现),并且icc可以摆脱它,而无论这是否是实际的bug。
最终,std::vector
将退回到使用更安全的选项,该选项是一个复制构造函数来重定位其元素(唯一指针的映射),但由于std::unique_ptr
不是CopyConstructible,因此会报告错误。
在另一方面,std::unique_ptr
此举构造被要求为noexcept
,[unique.ptr.single.ctor] :
unique_ptr(unique_ptr&& u) noexcept;
需要重定位时,唯一指针向量可以安全地移动其元素。
在较新的版本中stl_map.h
,地图的move构造函数具有以下用户提供的定义:
map(map&& __x)
noexcept(is_nothrow_copy_constructible<_Compare>::value)
: _M_t(std::move(__x._M_t)) { }
它明确地noexcept
仅取决于复制比较器是否抛出异常。
1从技术上讲,接受非常量l值引用的副本构造函数可以更改原始对象,例如std :: auto_ptr,但是MoveInsertable要求向量元素必须可以从r值构造,而不能绑定到非常量l-值。价值参考。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句