C++ 标准是不是允许在没有开销的情况下实现 std::optional<double>

Posted

技术标签:

【中文标题】C++ 标准是不是允许在没有开销的情况下实现 std::optional<double>【英文标题】:Does C++ standard allow std::optional<double> to be implemented without overheadC++ 标准是否允许在没有开销的情况下实现 std::optional<double> 【发布时间】:2019-03-26 10:54:01 【问题描述】:

我刚刚看了 cppcon 谈论 Bloomberg datum,变体类型使用 IEEE754 格式的冗余来编码存储在 datum 中的类型。

所以我想知道 C++ 标准是否允许实现通过使用相同的技巧更有效地实现 std::optional。

请注意,这需要有时存储在 optional 中的 double 的二进制表示与传递给构造函数的 double 的二进制表示不匹配。

注意事项: 我关心标准是否允许这样做,我知道大多数/所有实现都不会打扰。

我知道 IEEE754 不是标准强制要求的,但它是允许的并且可以通过实施检查。

【问题讨论】:

如果没有标准的明确禁止,那怎么可能不允许呢?请注意,UB 不适用于实现,就像它适用于用户代码一样。 “我知道大多数/所有实现都不会打扰” - 为什么会有负面影响? @StoryTeller STL 是一个庞大的库,而 std::optional 极少使用……而且大多数 STL 实现只有少数全职开发人员在工作。 @BaummitAugen 就像我说的那样,它破坏了值的二进制表示。也可能有其他我没有想到的问题,这就是我问的原因。 我没看过那个演讲,但是在 IEEE-754 下,浮点值中的 每个 位模式都是有意义的。 Many 值很少使用;信令 NaN 在日常编程中大多无用。但是通用库不能忽略它们的存在并使用它们来编码“非值”;这会破坏使用它们的(诚然很少见的)代码。 【参考方案1】:

标准要求,如果您将值存储在 std::optional 中,则该值必须能够完全按照存储的方式检索。此外,如果使用了optional&lt;T&gt;,您可以将任何 T 存储在optional 的值中,而不会让optional 知道您正在这样做。像这样:

optional<T> opt = T;
auto &&val = *opt;
val = <insert value here>; //opt has no idea it has been set.

因此,可以优化optional&lt;T&gt; 以使用T 的某些值来表示optional 未参与的唯一有效方法是用户不可能使用这些值创建一个Tdouble 的 IEEE-754 实现可以采用任何位模式,并且它们都是合法的(甚至是 NaN 信号)。

其他可选类型可以这样做的原因是因为它们与用户有一个隐含的协议,即它们不会将其设置为某些值。 std::optional&lt;T&gt;没有这样的约定; T 可以假设的任何值都可以存储和检索。

现在,如果 optional&lt;T&gt;::operator*optional&lt;T&gt;::value 返回某种代理对象而不是直接引用 T,那么这可能是可能的,因为代理可以处理适当的转换。但即便如此,该标准也必须明确指出,尝试将其设置为这些值之一将导致该值采用等效但不同的对象表示。

【讨论】:

Nitpick:也许你可以在你的答案中加入一个大大的 NO,因为很多人可能无法理解答案,因为它需要一些不平凡的 C++ 知识,例如 auto&& @NoSenseEtAl: auto&amp;&amp; 是人们需要成为increasingly familiar with 的东西,所以越早越好。此外,在 optional&lt;T&gt; 的情况下,它在功能上与 auto&amp; 没有什么不同。 我明白你的意思,但答案应该尽可能缩小 IMAO,以便初学者可以“获得” 1 个项目...无论如何,就像我说的那样,这是一个挑剔的问题。很好的答案,我没想到这个用例是可选的

以上是关于C++ 标准是不是允许在没有开销的情况下实现 std::optional<double>的主要内容,如果未能解决你的问题,请参考以下文章

在啥情况下我应该在 C++ 中使用 memcpy 而不是标准运算符?

C++ 是不是允许函数的默认返回类型?

alloca 是 C++ 标准的一部分吗?

用C++实现一个简易的线程池

是否允许 C++ 标准库实现加强 noexcept 规范?

C# 如何在 C++ 不允许虚拟模板方法的情况下允许虚拟泛型方法?