在映射内使用唯一的_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; } #包括 #包括 #包括 使用名称空间std; int main(int argc,char*argv[]) { 向量v; v、 调整大小(5); 返回退出成功; }

在映射内使用唯一的_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; } #包括 #包括 #包括 使用名称空间std; int main(int argc,char*argv[]) { 向量v; v、 调整大小(5); 返回退出成功; },c++,c++14,icc,C++,C++14,Icc,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::def

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"
/usr/local/[…]/include/c++/7.3.0/ext/new_allocator.h(136):错误:无法引用函数“std::pair::pair(const std::pair&)[with T1=const int,T2=std::unique_ptr]”(在/usr/local/[…]/include/c++/7.3.0/bits/stl_pair.h的第292行声明)——它是一个已删除的函数
{::新建((void*)uu p)u向上(std::转发(u参数));}
^
在以下期间检测到:
[...]
实例化“void std::vector::resize(std::vector::size_type={std::size_t={unsigned long})[with _Tp=std::map,_Alloc=std::分配器]”
在“program.cpp”的第10行
两个编译器编译以下代码时都没有问题

#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;
}
#包括
#包括
#包括
使用名称空间std;
int main(int argc,char*argv[])
{
向量v;
v、 调整大小(5);
返回退出成功;
}
英特尔编译器的第一个代码失败,因为它试图创建唯一的_ptr的副本,该副本仅定义移动构造函数。但是,我不确定第一个程序是否是合法的C++程序。
我想知道第一个代码是否错误,或者英特尔编译器是否存在缺陷。如果第一个代码是错误的,为什么第二个代码是正确的?或者第二个也是错误的?

问题源于
std::vector::resize
,的以下post条件:

备注:如果异常不是由非CopyInsertable
T
的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))), "!");
std::map
既可以移动也可以复制。这两种方法都不要求不引发异常

但是,允许实现提供这种保证{{需要引证}。您的代码使用以下定义:

map(map&&) = default;
隐式生成的移动构造函数是否必须是
noexcept

继承构造函数([class.inhctor])和隐式声明的特殊成员函数(子句[special])具有异常规范。如果
f
是继承构造函数或隐式声明的默认构造函数、复制构造函数、移动构造函数、析构函数、复制赋值运算符或移动赋值运算符,其隐式异常规范指定了类型id
T
当且仅当
T
f
的隐式定义直接调用的函数的异常规范允许时
f
允许所有异常,如果它直接调用的任何函数允许所有异常,
f
具有异常规范
noexcept(true)
如果它直接调用的每个函数不允许任何异常。

此时,很难说icc move构造函数隐式生成的是否应该是
noexcept
。无论哪种方式,
std::map
本身都不需要是可构造的,因此它更像是一个实现质量问题(库的实现或隐式生成构造函数的实现),icc可以解决它,不管这是否是一个实际的bug

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

另一方面,
std::unique_ptr
的移动构造函数必须是
noexcept
,:

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


在较新版本的
stl_map.h
中,有以下用户提供的地图移动构造函数定义:

map(map&& __x)
  noexcept(is_nothrow_copy_constructible<_Compare>::value)
  : _M_t(std::move(__x._M_t)) { }
map(map&&ux)
noexcept(是可构造的::值)
:_M_t(std::move(_x._M_t)){}
这使得
noexcept
仅依赖于是否复制比较器抛出



1从技术上讲,接受非常量l值引用的复制构造函数可以更改原始对象,例如std::auto_ptr,但MoveInsertable要求向量元素可以从r值构造,无法绑定到非常量l值引用。

在我看来像是一个英特尔错误。很可能英特尔没有将
的移动构造函数标记为
noexcept
。如果这是真的,我可以重铸
unique_ptr(unique_ptr&& u) noexcept;
map(map&& __x)
  noexcept(is_nothrow_copy_constructible<_Compare>::value)
  : _M_t(std::move(__x._M_t)) { }