std :: unordered_map <T,std :: unique_ptr <U >>可复制?GCC错误?

Rob*_*ahy 12 c++ gcc g++ language-lawyer c++14

g++ --version 收益率:

g++.exe (x86_64-posix-seh-rev0, Built by MinGW-W64 project) 4.9.1
Copyright (C) 2014 Free Software Foundation, Inc.
This is free software; see the source for copying conditions.  There is NO
warranty; not even for MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
Run Code Online (Sandbox Code Playgroud)

程序:

#include <memory>
#include <type_traits>
#include <unordered_map>

static_assert(!std::is_copy_constructible<std::unordered_map<int,std::unique_ptr<int>>>::value,"Copyable");

int main () {   }
Run Code Online (Sandbox Code Playgroud)

编译结果:

.\unorderedmapcopyable.cpp:5:1: error: static assertion failed: Copyable
 static_assert(!std::is_copy_constructible<std::unordered_map<int,std::unique_ptr<int>>>::value,"Copyable");
 ^
Run Code Online (Sandbox Code Playgroud)

相关标准:

关于容器可复制

为语句X u(a)X u=a是有效的,对于一些容器类型X,它包含类型T,其中a是类型的值X:

要求: TCopyInsertableX

§23.2.1[container.requirements.general]

我对此的理解:如果T(在我们的例子中std::pair<const int,std::unique_ptr<int>>)没有CopyInsertable进入X(在我们的情况下std::unordered_map<int,std::unique_ptr<int>>),那么X u(a)并且X u=a形式不正确.

CopyInsertable

TCopyInsertableX手段的是,除了TMoveInsertableX,以下表达式是公形成:

allocator_traits<A>::construct(m, p, v)

并且它的评估导致以下后置条件保持:值v不变并等于*p.

我对此的理解: std::pair<const int,std::unique_ptr<int>>不是CopyInsertable,因为事实std::unique_ptr<int>是不可复制的:

Uunique_ptr本子条款[...]中指定的模板实例化的每个类型的对象都不CopyConstructibleCopyAssignable.

§20.8.1[unique.ptr]

并且由于复制构造函数std::pair<const int,std::unique_ptr<int>>是默认的:

pair(const pair&) = default;

§20.3.2[pairs.pair]

并且由于std::pair<const int,std::unique_ptr<int>>具有以下类型的成员std::unique_ptr<int>:

template <class T1, class T2> struct pair {

[...]

T2 second;

§20.3.2[pairs.pair]

并且由于默认情况下删除了默认的复制构造函数,而不是类型的所有成员都是CopyConstructible:

X如果X具有以下内容,则将类的默认复制/移动构造函数定义为已删除:

[...]

  • 类型M(或其数组)的非静态数据成员,由于应用于M相应构造函数的重载解析,无法复制/移动,导致[...]删除的函数[...]

§12.8[class.copy]

std::is_copy_constructible

对于可引用类型T,结果与is_constructible<T,const T&>::value否则相同false.

§20.10.4.3[meta.unary.prop]

我对此的理解/阅读: std::is_copy_constructible<std::unordered_map<int,std::unique_ptr<int>>与之相同std::is_constructible<std::unordered_map<int,std::unique_ptr<int>,std::unordered_map<int,std::unique_ptr<int> &>.

std::is_constructible

给出以下函数原型:

template <class T> add_rvalue_reference_t<T> create() noexcept;

is_constructible<T, Args...>当且仅当以下变量定义适用于某些发明变量时,才应满足模板特化的谓词条件t:

T t(create<Args>()...);

§20.10.4.3[meta.unary.prop]

我对此的理解: std::is_constructible<std::unordered_map<int,std::unique_ptr<int>>,std::unordered_map<int,std::unique_ptr<int> &>应该是std::false_type,而不是std::true_type,因为X u(a)没有良好的形式.

我的问题

是否应接受上述代码?这是一个GCC/libstdc ++错误,还是我缺少标准中的某些内容?

我目前无法访问Clang或MSVC++,否则我会对它们进行测试.

T.C*_*.C. 9

您的分析中存在两个问题.

首先,违反Requires子句会导致未定义的行为(第17.6.4.11节[res.on.required]):

违反函数的Requires: 段中指定的前提条件会导致未定义的行为,除非函数的 Throws: paragraph指定在违反前提条件时抛出异常.

这意味着如果您尝试unordered_map使用非CopyInsertable元素复制构造,则库可以执行任何操作.它不一定会导致程序格式不正确(尽管它可能会在复制构造函数的实现中深处).

其次,由is_constructible特征执行的测试仅限于直接上下文(§20.10.4.3[meta.unary.prop]/p7,强调添加):

执行访问检查就好像在与其无关的上下文T中执行Args.仅考虑变量初始化的直接上下文的有效性.[ 注意:初始化的评估可能会导致副作用,例如类模板特化和函数模板特化的实例化,隐式定义函数的生成等等.这种副作用不在"直接背景"中,并且可能导致程序形成不良.- 结束说明 ]

换句话说,这基本上只考虑是否存在匹配的,可访问的和未删除的构造函数签名,而不是实例化构造函数将导致格式良好的代码.

标准必须指定容器的复制构造函数,其中包含"如果T不是CopyInsertable,则此构造函数不应参与重载解析X"以保证is_copy_constructible特征的行为符合您的要求.标准中没有这样的规范.

正如Marc Glisse在评论中写道,虽然这不是标准规定的,但它可以被认为是一个实施质量问题,因此错误报告是合理的.


编辑:我想到,从非CopyInsertable元素的重载解析中删除复制构造函数的要求可能是不可实现的,因为该属性是根据对allocator_traits<A>::construct(m, p, v)格式良好并具有所需语义的调用来指定的.我不相信SFINAE可以决定呼唤身体的良好形态allocator_traits<A>::construct().