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

Posted

技术标签:

【中文标题】具有显式构造函数的类是不是需要在 emplace 中使用分段构造?【英文标题】:Do classes with explicit constructors require piecewise_construct in emplace?具有显式构造函数的类是否需要在 emplace 中使用分段构造? 【发布时间】:2016-12-11 23:11:34 【问题描述】:

如果我使用 explicit 构造函数创建结构

struct A 
    int x;
    explicit A(int x):x(x);
;

然后将它用作std::map 中的mapped_type,我就可以使用分段构造函数来代替:

#include <map>
std::map<int, A> foo;

foo.emplace(
    std::piecewise_construct, 
    std::forward_as_tuple(1), 
    std::forward_as_tuple(10)
    );

但是当我尝试使用移动或模板构造函数时,我得到错误并且无法编译:

foo.emplace(std::make_pair(2, 20)); // <-- doesn't work
foo.emplace(3, 30);                 // <-- doesn't work

这里发生了什么?直到现在,我还没有意识到这些不同的用法之间存在很大差异。我想,使用 pair move 构造函数,从std::pair&lt;int, A&gt;... 进行隐式转换可能是有意义的,但为什么必须在模板构造函数中发生这种情况?那么为什么使用分段构造函数呢??

我查了一下,但 std::map::emplaceexplicit 的文档并没有真正为我澄清这一点。

【问题讨论】:

编译器/版本? gcc 版本 4.8.4 (Ubuntu 4.8.4-2ubuntu1~14.04.3) 【参考方案1】:

在N4387 之前,pair&lt;T1, T2&gt; 的构造函数采用U/V do not exist 除非U 可以隐式转换为T1 并且V 可以隐式转换为T2

template<class U, class V> constexpr pair(U&& x, V&& y);

要求: is_constructible&lt;first_type, U&amp;&amp;&gt;::valuetrueis_constructible&lt;second_type, V&amp;&amp;&gt;::valuetrue

效果:构造函数用std::forward&lt;U&gt;(x)初始化first,用std::forward&lt;V&gt;(y)初始化second

备注: 如果U 不能隐式转换为first_typeV 不能隐式转换为second_type,则此构造函数应 不参与重载决议。

const pair&lt;U, V&gt;&amp;pair&lt;U, V&gt;&amp;&amp; 构造函数也是如此。

由于这些构造函数实际上不存在,你后面的两个emplaces 将不起作用。


N4387 更改了这里的规则,如果这两种类型都可以从相应的参数类型构造,但至少有一种不能从参数类型隐式转换,那么这些构造函数将变为 explicit。因此,在 C++17 中,所有三个 emplaces 都将编译。另外,正如论文addresses a defect report raising pretty much this exact issue(其中几篇),实现也可以选择在早期的标准模式下实现它。

【讨论】:

以上是关于具有显式构造函数的类是不是需要在 emplace 中使用分段构造?的主要内容,如果未能解决你的问题,请参考以下文章

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

为啥我会使用 push_back 而不是 emplace_back?

访问控制对已删除的构造函数是不是重要?

[CPP]push_back和emplace_back的区别

构造函数和析构函数

标准库 `emplace` 函数是不是使用 `std::in_place` 标记