为啥 std::optional 构造函数使用 std::in_place?

Posted

技术标签:

【中文标题】为啥 std::optional 构造函数使用 std::in_place?【英文标题】:Why do std::optional constructors use std::in_place?为什么 std::optional 构造函数使用 std::in_place? 【发布时间】:2018-09-20 22:01:42 【问题描述】:

一些std::optional 构造函数使用std::in_place_t 标记参数,如下所示:

template< class... Args > 
explicit optional( std::in_place_t, Args&&... args );

我看到这样的构造函数可以在没有就地标记的情况下实现,并使用一些enable_if (SFINAE) 魔法来不参与不情愿的重载,即:

template< class... Args > 
explicit optional( Args&&... args );

为什么std::optional 的就地构造函数使用std::in_place_t 标签而不是一些enable_if 魔术(并且没有标签)实现?

更新:稍微更新了问题,以强调我意识到简单地省略就地标记是行不通的。

【问题讨论】:

匹配任何东西。 @Cheersandhth.-Alf 为什么这是个问题? 假设T 有一个隐式转换构造函数,它接受一个类型不等于T 的单个参数。然后,您将创建一个临时值,而不是将该值用于就地构造,而没有机会更改它。 @MatthäusBrandl 但你可以通过 SFINAE 来阻止这种情况。实际上,无论有无标签,都只转发一个参数。没有创建临时对象 据我所知,假设的无标签版本失败的唯一情况是默认构造包含的值 【参考方案1】:

正如Passer By 在comment 中所说,其目的是为了消除人们想要调用optional&lt;T&gt; 的默认构造函数和想要调用T 的默认构造函数的情况的歧义。

此意图在N3527 中提出,其中in_place_t 的原始建议名称为emplace。我在这里引用相关部分:

我们需要额外的标签来消除某些情况的歧义,比如调用optional的默认构造函数和请求T的默认构造函数:

optional<Big> obemplace, "1"; // calls Big"1" in place (no moving)
optional<Big> ocemplace;      // calls Big in place (no moving)
optional<Big> od;             // creates a disengaged optional

【讨论】:

如何搜索论文/提案? @0x499602D2 我用关键字“C++ in_place_t paper”搜索,找到N3793,然后找到N3527。 @0x499602D2 如果您有权访问 CppLang Slack,您可以向 npaperbot 发送消息。真的很棒! @xskxzr 如果 std::optional&lt;T&gt; 构造函数输入 2 个或更多参数,难道不能总是假设这些需要转发给 T's 构造函数,因此不需要 in_place_t 吗? @user3882729 2 个或多个 args 的接口与 1 个 args 的接口不同,这很奇怪。

以上是关于为啥 std::optional 构造函数使用 std::in_place?的主要内容,如果未能解决你的问题,请参考以下文章

为啥 std::optional<int> 的构造比 std::pair<int, bool> 更昂贵?

为啥 std::optional 对 std::nullopt 类型的操作数有一个特殊的相等运算符

为啥 std::optional::value() &&;返回 &&?

std::optional - 用 或 std::nullopt 构造空?

为啥允许将 std::optional 与值进行比较? [复制]

为啥 const rvalue 限定 std::optional::value() 返回 const rvalue 引用?