在地图内使用unique_ptr时std :: pair中已删除的函数

里蒂奇

我有一段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::mapMove-CopyConstructible都不要求不引发异常。

但是,允许实现提供此保证{{citation needed}}}您的代码使用以下定义:

map(map&&) = default;

是否需要隐式生成的move构造函数noexcept[except.spec]

继承构造函数([class.inhctor])和隐式声明的特殊成员函数(Clause [special])具有异常规范如果f是继承的构造函数或隐式声明的默认构造函数,复制构造函数,move构造函数,析构函数,复制赋值运算符或移动赋值运算符,则其隐式异常规范且仅当异常规范允许时指定type-id 的隐式定义直接调用的函数如果它直接调用的任何函数允许所有异常,并且具有异常规范,则允许所有异常TTfff 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] 删除。

编辑于
0

我来说两句

0 条评论
登录 后参与评论

相关文章

在移动构造函数中的unique_ptr上调用std :: move时出现“错误:使用已删除的函数”

使用已删除的函数unique_ptr

奇怪的错误:当未真正创建指针时,使用删除的函数'std :: unique_ptr <_Tp,_Dp> :: unique_ptr

std :: unique_ptr尝试引用已删除的函数

使用地图内的std :: unique_ptr作为键

std :: unique_ptr <T []>具有派生对象的数组,使用已删除的函数

合并unique_ptr的两个向量时的“使用已删除函数”

使用`std :: unique_ptr`时`std :: vector`中的数据不同

使用operator ==时std :: set中unique_ptr的深度比较

使用 std::unique_ptr<T>::unique_ptr` 在 Visual Studio 2013 中构建错误

将std :: list <std :: unique_ptr>移至向量尝试引用已删除的函数

当unique_ptr在向量中时,对std :: unique_ptr <>拥有的对象的引用/ ptr是否安全?

使用std :: map时,包含unique_ptr的结构中的默认析构函数会导致编译错误

C ++ std :: unique_ptr存储在std :: map内部,使用删除的函数会形成不良

Unique_Ptr:尝试引用已删除的函数

std :: unique_ptr constexpr构造函数

std :: unique_ptr构造函数的行为

包装器使用普通函数删除器构建std :: unique_ptr

C ++将删除的函数std :: unique_ptr与基类一起使用

删除函数unique_ptr

使用自定义删除器在地图中存储unique_ptr

使用std :: function对象将自定义删除器传递给std :: unique_ptr

无法使用带有std :: move的自定义删除器插入std :: unique_ptr

是否可以使用fill构造函数创建std :: vector <std :: unique_ptr <Bar >>?

双向链接列表std :: unique_ptr类在删除节点时无法按预期工作

当使用由std :: unique_ptr定义的一元运算时,term不会在std算法中求值为1自变量

錯誤:使用已刪除的函數 std::unique_ptr

如何使用删除器调用unique_ptr构造函数?

使用已使用 new X() 创建的变量将 std::unique_ptr 传递给函数