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
CopyInsertable
为X
§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
表示X
除T
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 ++,否则我会对它们进行测试。
答案 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
,那么此构造函数不应参与重载解析/ em>保证is_copy_constructible
特征的行为符合您的要求。标准中没有这样的规范。
正如Marc Glisse在评论中写道,虽然这不是标准规定的,但它可以被认为是一个实施质量问题,因此错误报告是合理的。
编辑:我想到,从非CopyInsertable
元素的重载解析中删除复制构造函数的要求可能无法实现,因为该属性是根据对allocator_traits<A>::construct(m, p, v)
的调用指定的。形式良好,具有所需的语义。我不相信SFINAE可以确定allocator_traits<A>::construct()
号召唤正文的形式。