gpt4 book ai didi

c++ - 在 map 中使用 unique_ptr 时 std::pair 中的已删除函数

转载 作者:行者123 更新时间:2023-12-01 14:57:36 30 4
gpt4 key购买 nike

我有一段 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] :

Remarks: If an exception is thrown other than by the move constructor of a non-CopyInsertable T there are no effects.



也就是说,如果重定位失败, vector 必须保持不变。重定位可能失败的原因之一是由于异常,特别是当用于将元素从旧存储转移到新存储的复制或移动构造函数引发异常时。

复制元素是否会以任何方式改变原始存储? 1号。移动元素会改变原始存储吗?是的。哪种操作更高效?移动。 vector 总是喜欢移动而不是复制吗?不总是。

如果移动构造函数可以抛出异常,则无法恢复旧存储的原始内容,因为尝试将已移动的元素移回旧 block 可能会再次失败。在这种情况下, vector 将使用移动构造函数将其元素从旧存储重新定位到新存储,前提是该移动构造函数保证它不会抛出异常(或者移动构造函数是复制构造函数时的唯一选择)无法使用)。一个函数如何保证它不会抛出异常?一个将使用 noexcept 进行注释。说明符并使用 noexcept 进行测试运算符(operator)。

使用 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既可移动又可复制。两者都不需要不抛出异常。

但是,允许实现提供此保证 {{citation needed}}。您的代码使用以下定义:
map(map&&) = default;

隐式生成的移动构造函数是否必须为 noexcept ? [except.spec] :

An inheriting constructor ([class.inhctor]) and an implicitly declared special member function (Clause [special]) have an exception-specification. If f is an inheriting constructor or an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f's implicit definition; f allows all exceptions if any function it directly invokes allows all exceptions, and f has the exception-specification noexcept(true) if every function it directly invokes allows no exceptions.



此时,很难说icc move构造函数隐式生成的是否应该是 noexcept或不。无论哪种方式, std::map它本身不需要是 nothrow-MoveConstructible,所以它更多的是实现质量问题(库的实现或构造函数的隐式生成的实现),不管这是否是一个实际的错误,icc 都会摆脱它。

最终, std::vector将回退到使用更安全的选项,即复制构造函数来重新定位其元素(唯一指针的映射),但由于 std::unique_ptr不是CopyConstructible,报错。

另一方面, std::unique_ptr的移动构造函数必须是 noexcept , [unique.ptr.single.ctor] :
unique_ptr(unique_ptr&& u) noexcept;

当需要重定位时,唯一指针 vector 可以安全地移动其元素。

stl_map.h 的较新版本中 map 的移动构造函数有以下用户提供的定义:
map(map&& __x)
noexcept(is_nothrow_copy_constructible<_Compare>::value)
: _M_t(std::move(__x._M_t)) { }

明确地使 noexcept仅取决于复制比较器是否抛出。

1 从技术上讲,接受非 const 左值引用的复制构造函数可以更改原始对象,例如 std::auto_ptr,但 MoveInsertable 要求 vector 元素可以从 r 值构造,不能绑定(bind)到非 const l-值(value)引用。

关于c++ - 在 map 中使用 unique_ptr 时 std::pair 中的已删除函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/62075610/

30 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com