的std :: unordered_map <t时,标准::的unique_ptr <U>&GT;可复制? GCC错误?</t,std :: unique_ptr <u>

时间:2014-09-12 07:54:16

标签: 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.

程序:

#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 () {   }

编译结果:

.\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");
 ^

相关标准:

关于容器可复制

对于语句X u(a)X u=a有效,对于某些包含类型X的容器类型T,其中a的值为输入X

  

要求: T CopyInsertableX

     

§23.2.1[container.requirements.general]

我对此的理解:如果T(在我们的情况下是std::pair<const int,std::unique_ptr<int>>CopyInsertable不是X(在我们的案例中{{}} 1}}),然后std::unordered_map<int,std::unique_ptr<int>>X u(a)格式不正确。

X u=a

  

CopyInsertable T CopyInsertable表示XT MoveInsertable X之外,还有以下表达式:形成的:

     

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

     

及其评估会导致以下后置条件成立:v的值保持不变,相当于*p

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

  

从本子条款[...]中指定的U模板实例化的unique_ptr类型的每个对象都不是CopyConstructible也不是CopyAssignable

     

§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 ++,否则我会对它们进行测试。

1 个答案:

答案 0 :(得分:9)

您的分析存在两个问题。

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

  

违反函数中指定的先决条件需要:   段落导致未定义的行为,除非函数的   抛出:段落指定在违反前提条件时抛出异常。

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

其次,由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()号召唤正文的形式。