【问题标题】:Type design: value types, default-constructibility, optional<T> and its relationship?类型设计:值类型、默认可构造性、可选<T>及其关系?
【发布时间】:2014-10-29 04:09:45
【问题描述】:

最近我看到很多关于泛型编程的资料,但在设计类型时,我仍然无法理解一件事。我不确定什么是最好的方法,让我解释一下。

对于某些类型,提供默认构造函数是很自然的。 该类型的所有可能构造都是有效的,或者默认值是有意义的,因此提供默认值是有意义的。这是基本类型的情况。

后来,有些类型默认构造它们不会产生值。例如,在标准库中,我们有 std::function&lt;Sig&gt;std::thread。然而,它们是默认可构造的,即使它们没有持有值。

后来,我们在标准中提出了optional&lt;T&gt;。将它用于基本类型很有意义,因为对于基本类型,所有可能的赋值都表示一个有效值(double 和 float NaN 除外),但我不知道您将如何将它用于 threadstd::function&lt;Sig&gt;,因为这些类型在构造时不包含“值”。就像这些类型直接嵌入在类型中的“可选”一样。

这有这些缺点。由于没有“自然”的默认(或值)构造,例如 int:

  • 现在我必须在我的所有设计中用if (valid) 乱扔我的班级并发出错误信号。或
  • 如果我不进行此检查,则使用起来会不太安全。前提条件 -> 如果默认构造,则在使用前分配。

所以当我想设计一个类型时,我总是会发现一个问题:我应该让它默认可构造吗?

优点:

  • 更容易在更通用的上下文中重用,因为如果我添加适当的操作,我的类型将更容易建模 SemiRegular 或 Regular。

缺点:

  • if 声明在整个班级中乱扔垃圾,或者与用户签订合同,其中班级使用起来更不安全。

例如,假设我有一个带有 ID、艺术家、标题、持续时间和年份的课程 Song。标准库使类型默认可构造是非常好的。但是:

  • 我无法找到一种自然的方式来构建“默认歌曲”。
  • 我不得不乱扔if (validsong),否则使用起来不安全。

所以我的问题是:

  1. 我应该如何设计一个没有“自然(如值)”默认值的类型?我是否应该提供默认构造函数?

  2. 在我选择提供默认构造函数的情况下,optional&lt;T&gt; 如何适应所有这些难题?我的观点是,让一个不是“自然”默认可构造的类型提供默认构造函数会使optional&lt;T&gt; 在这种情况下毫无用处。

  3. optional&lt;T&gt; 是否应该只用于值域完整的类型,也就是说,我不能为其表示分配无效值,因为它们都包含一个值,例如 int?

  4. 为什么在标准中首先将std::function&lt;Sig&gt; 等类型设为默认可构造?构造时,它不保存值,所以我不明白为什么应该提供默认构造函数。你总是可以这样做:optional&lt;function&lt;void ()&gt;&gt;,例如。这只是一个设计选择,两者都是有效的,还是有一个设计,在这种情况下,关于选择默认与非默认可构造优于另一个?

【问题讨论】:

  • 实际上,我对 Stepanov 和 Sean Parent 对这些问题的看法非常感兴趣。有什么材料吗? P.S.:编程要素没看完,可能有回复。

标签: c++ optional default-constructor


【解决方案1】:

(注意:一个问题中有很多问题的一个问题是它的某些部分可能是重复的。最好问一些较小的问题,并检查每个问题是否有以前的帖子。“每个问题一个问题”很好政策;有时说起来容易做起来难,我猜。)

  1. 为什么 std::function 等类型在标准中首先被设为默认可构造?构造时,它不保存值,所以我不明白为什么应该提供默认构造函数。你总是可以这样做:optional&lt;function&lt;void ()&gt;&gt;,例如。

Why do std::function instances have a default constructor?

  1. 我应该如何设计一个没有“自然(如值)”默认值的类型?我是否应该提供默认构造函数?

类型的默认构造函数很难在没有某种数据的情况下有意义地定义自己,这就是许多类实现空值的方式。可选是更好的选择吗?我通常这么认为,但我假设你知道std::optionalvoted out of C++14。即使它是完美的答案,它也不能成为每个人的答案......它还不是汤。

它总是会增加一些开销来运行时跟踪值是否绑定。也许不是很多的开销。但是,当您使用一种语言,其存在的理由是允许抽象,同时仍然让您在尽可能靠近金属的地方开枪......在巨大的向量中为每个值削减一个字节可能很重要。

因此,即使optional&lt;T&gt; 语义和编译时检查是完美的,您仍然可能会面临这样一种情况,即废弃它并允许您的类型对其自身的无效性进行编码是有利的。必须推动那些像素或多边形或数据包或... pfafftowns。

  1. 在我选择提供默认构造函数的情况下,可选如何适应所有这些难题?我的观点是,在这种情况下,让一个不是“自然”默认构造的类型提供默认构造函数会使可选的无用。

  2. 应该可选只用于值域完整的类型,这意味着,我不能为其表示分配无效值,因为它们都包含一个值(我猜除了 float 和 double NaN)。

在我自己的情况下,我发现自己想在编译时检查可以处理空指针的例程和不能处理空指针的例程之间的区别。但是突然optional&lt;pointer&gt; 提供了这种情况,即可选的未绑定、绑定到空指针和绑定到非空指针。编译时健全性检查似乎不太成功。

那么可选引用呢?它们是有争议的,以至于我上次听说它们是从 C++14 延迟 std::optional 的一系列事情中的症结之一。在我将可选指针转换为可选引用之后,这有点烦人。 :-/

我有一个模糊的想法,要写一本关于“病态 C++”的书,你可以在其中挑选一些想法并开始将其得出合乎逻辑的结论。 optional&lt;T&gt; 是我的一脚,基本上遵循了您确定的原则。消除在类型本身中编码“nullity”的可能性,然后您突然可以让编译器进行类型检查,以检查给定的代码是否准备好预期为 null。

(现在我倾向于怀疑,如果你对这种“病态 C++”非常着迷,你最终会重新发明 Haskell。:-/ 参见流行的 Data.Maybe monad。)

【讨论】:

  • 我认为这些问题密切相关,如果我将它们分开,实际上我会失去重点。您必须将其视为“所有相关事物” -> 默认构造、可选、规律性和权衡。
  • @GermánDiago 当然...我们可以看到所有编程都是相关的,所以 StackOverflow 上的所有内容都指向一个大问题。这就是为什么问答中的挑战是将一个大问题分解为独立可重复使用的机构知识片段。就像软件中的挑战一样。 “建筑的本质是对手头任务不必要的信息的压制,因此建筑的本质是这样的,它从不向我们展示它的全部自我,而只是一两个方面,这在某种程度上是合适的一次。” :-)
  • 感谢您的建议。我有强烈的意见,在这种情况下,它现在是有意义的。
  • @GermánDiago 还要考虑我的强烈意见,如果你发现这种问题让你热情洋溢而不是分散注意力......你可能会坐下来 Learn You a HaskellTypeclassopedia 和意识到 C++ 并没有磨削这个特殊的斧头,除非是一时兴起的爱好。如果您在这里过于用力地推动 C++,您要么需要以不同的方式思考您的优先事项是什么,只是少考虑一般性,或者切换语言。 :-)
  • 我确实喜欢 Haskell,但我发现以仅功能的方式编写代码很尴尬。不过,我承认功能强大。另一方面,c/c++ 的生态系统/社区是如此之大,以至于它在库方面几乎是无与伦比的 :)
猜你喜欢
  • 1970-01-01
  • 2012-04-14
  • 1970-01-01
  • 2021-05-29
  • 2018-10-04
  • 1970-01-01
  • 1970-01-01
  • 2018-10-14
相关资源
最近更新 更多