为啥 std::map emplace 需要 gcc 上的复制构造函数?

Posted

技术标签:

【中文标题】为啥 std::map emplace 需要 gcc 上的复制构造函数?【英文标题】:Why does std::map emplace need a copy constructor on gcc?为什么 std::map emplace 需要 gcc 上的复制构造函数? 【发布时间】:2017-06-22 12:33:24 【问题描述】:

以下代码在 Visual Studio 的 Visual C++ 19.0 编译器上编译良好,但 gcc 5.4.0 抱怨复制构造函数是私有的。

#include <map>

class CMyClass

public:
  CMyClass(int) ;
private:
  CMyClass(const CMyClass&); // I want to avoid copy construction
;

int main()

  std::map<int, CMyClass> mymap;
  mymap.emplace(0, 0);

  return 0;

错误信息:

‘CMyClass::CMyClass(const CMyClass&)’ is private

避免复制不正是emplace 的用途吗?我错过了什么吗?

【问题讨论】:

所以它需要一个复制构造函数但不会使用它? 看起来像 gcc 5.4 中的一个错误。相同的代码compiles with gcc 6。 @Bert 对不起,我的错。在某些情况下需要复制构造函数 【参考方案1】:

这在已发布的 C++11 中无效。

C++11 标准描述了两个带有两个参数的 pair 构造函数:

pair(const T1& x, const T2& y);
template<class U, class V> pair(U&& x, V&& y);

如果第一个被选中,那么由于显而易见的原因,你注定要失败。

如果与此处相关的“V 不能隐式转换为second_type”,则第二个重载不参与重载决议。这里,Vintsecond_typeCMyClassint 是否可以隐式转换为 CMyClass?不,因为声明 CMyClass x = some_int; 的格式不正确,就像在 C++11 中,该声明名义上从 some_int 构造了一个 CMyClass 临时,然后将其移动到 x 中,但 CMyClass 不能移动。

GCC 添加的额外重载 - 在允许的情况下 - 具有类似的约束。


C++17 大幅修改了pair 的构造函数的约束,这具有允许您的代码工作的副作用。实施者将此视为追溯缺陷修复,这就是 GCC 6 接受您的代码的原因。

【讨论】:

好的,但是你怎么能这样做呢?有没有办法在 std::map (GCC 【参考方案2】:

GCC 5.4.0 仅在启用相关重载时检查类型是否可转换:

  // DR 811.
  template<class _U1, class = typename
       enable_if<is_convertible<_U1, _T1>::value>::type>
constexpr pair(_U1&& __x, const _T2& __y)
: first(std::forward<_U1>(__x)), second(__y)  

而最新版本的 GCC 会检查它是否可移动构造:

288       template<typename _U1, typename
289            enable_if<_PCCP::template
290                _MoveCopyPair<true, _U1, _T2>(),
291                          bool>::type=true>
292        constexpr pair(_U1&& __x, const _T2& __y)
293        : first(std::forward<_U1>(__x)), second(__y)  

特别是_PCCP_PCC&lt;true, _T1, _T2&gt; 的别名。 _PCC 是一个包含这个函数的特质类:

138       template <bool __implicit, typename _U1, typename _U2>
139       static constexpr bool _MoveCopyPair()
140       
141     using __do_converts = __and_<is_convertible<_U1&&, _T1>,
142                   is_convertible<const _U2&, _T2>>;
143     using __converts = typename conditional<__implicit,
144                        __do_converts,
145                        __not_<__do_converts>>::type;
146     return __and_<is_constructible<_T1, _U1&&>,
147               is_constructible<_T2, const _U2&&>,
148               __converts
149               >::value;
150       

【讨论】:

这里使用的pair 是错误的重载;正确的重载应该采用_U2 推导类型,隐式转换为_T2,而不是T2 以进行复制。应该从考虑中消除这种过载。无效 ctor 的存在并不能证明不存在有效 ctor,也不应该考虑这个无效 ctor 并产生硬错误。 @Yakk 注意is_convertible&lt;int, CMyClass&gt;在C++17之前为false,因为私有拷贝,没有移动;在条件显式构造函数调整 SFINAE 约束之前,该重载被禁用。 @Yakk 不,不是。并非没有“保证省略”。 @T.C.哦,我在想CMyClass test() return int(7); ,我的错

以上是关于为啥 std::map emplace 需要 gcc 上的复制构造函数?的主要内容,如果未能解决你的问题,请参考以下文章

C++ 二维 map vector 赋值 遍历 实例 降序

C++ 二维 map vector 赋值 遍历 实例 降序

具有显式构造函数的类是不是需要在 emplace 中使用分段构造?

为啥调用 std:map:clear 后内存占用率没有降低

gcc std::unordered_map 实现速度慢吗?如果是这样 - 为啥?

为啥我插入 std::map 失败?