【问题标题】:Why can't I construct optional<T> with an optional<S> (for S!=T)?为什么我不能用 optional<S> 构造 optional<T>(对于 S!=T)?
【发布时间】:2016-05-02 12:58:37
【问题描述】:

如果我们有

 std::experimental::optional<int> x;

以下行都不编译:

 std::experimental::optional<unsigned int> y; y = x;
 std::experimental::optional<unsigned int> z(x);

...尽管在我看来就像将int 分配给unsigned int 一样有意义。为什么不应该这样做?也就是说,在这种情况下,如果库不实现复制 ctor 和赋值运算符,可以避免什么陷阱?

【问题讨论】:

  • 目前似乎唯一优雅的方式来构造一个可选项是通过move assignment operator。原因是它很可能是所需的行为。该类型还支持通过initializer list 直接(但有点混乱)构造。
  • @DavidPacker:我没有听懂你在说什么......你提供的链接是当前可用的赋值运算符;其中不包括其他可选类型之一。你能解释一下为什么这与搬家任务特别相关吗?
  • 抱歉,我没有检查第四步赋值运算符的详细信息。似乎甚至不允许分配不同类型的选项。确实没有任何文档可以证明原因,唯一可以给出答案的可能是负责创建构造的团队成员。
  • 好吧,公平地说,这是因为 C++ 语言很棒,但它的标准库却不是。当我理解它的那一刻,我作为 C++ 开发人员的生活变得更加简单和美好。你在抱怨可选吗?我首先要抱怨 2016 年的 IOStream 库、字符串库以及缺乏好的、开箱即用的自上而下的标准库。好消息是,在 C++ 中,你可以自己编写任何东西,而无需等待新标准发布。作为旁注,您可以随时转向 boost-optional 或 facebook folly's optional
  • @DavidHaim:好吧,如果标准库中没有 no 可选选项,那将是一回事,但既然它被采用了,问题仍然是为什么没有实现转换也?如果您认为标准库中的选择通常是不幸的/不合理的/差的,您是否可以链接到某个地方,使该论点更加实质性?

标签: c++ optional c++-standard-library c++17


【解决方案1】:

您不能这样做,因为std::experimental::optional 没有适合构造函数和赋值运算符的重载。

当然可以提供这样的接口。事实上,所提议的接口所基于的 boost optional 确实有一个重载的构造函数和赋值运算符,用于从optional 的另一个实例进行构造。

proposal 有一个“与 Boost.Optional 比较”部分,其中包含一个比较表,其中包括讨论的重载。除了一般性声明之外,该提案没有说明差异的理由:

当前的提议反映了我们在界面的明确性、通用性和灵活性之间任意选择的平衡。

决定是否包含重载的基本原理在具有参考实现的git repoISO C++ Standard - Future Proposals 邮件列表中进行了讨论。

在 github 问题中,该提案的作者 Andrzej Krzemieński 展示了过载可能导致的这种有问题的歧义:

只是不清楚在以下情况下应该发生什么:

struct Tool
{
  Tool(int); // ctor 1
  Tool(optional<int>); // ctor 2
};

optional<int> oi = 1;
optional<Tool> ot = oi;

应该调用 Tool 的哪个构造函数以及为什么?

【讨论】:

  • 不清楚在这种情况下应该发生什么?啊,“你是想从optional&lt;int&gt;构造内部Tool,还是从optional&lt;int&gt;构造optional&lt;Tool&gt;
  • @Yakk 我认为这是歧义的本质。
  • @Yakk:完全同意你的看法。我认为非常清楚的是,当使用 optional 时,两个 ctor 有责任采取完全相同的行动,因此模棱两可是错误的,好吧,要是我有时间进行标准游说就好了……跨度>
  • @einpoklum 两个演员的行为方式相同不是optional 的业务。 optional 的业务是打电话。我认为您会从 monads 中获取一页并将其设为 int ctor,但我可以看到相反的论点。
  • @Yakk:无论他们选择调用哪个都可以,因为如果不是它所期望的,那就是“工具的问题”。编写工具的人负责编写代码,因此这无关紧要。我不熟悉 monads(尽管我听说过提到的概念),但我想说让一个 ctor 采用可选项是“好像”你有一个 ctor 采用一个 int 和一个 ctor 采用一个 nullopt,即像Scala 中的模式匹配。
【解决方案2】:

这似乎是反对转换构造的基本论点(作者姓名不清楚,但来自this thread):

我们有两个构造函数:

  • 拆箱optional&lt;T&gt; 来自optional&lt;U&gt;,如果可选已初始化,则从U 构造T
  • 转发:来自U 的可选,它从U 构造T

问题是如果T 可以同时从Uoptional&lt;U&gt; 构造,我们就有歧义,现在我们可以 选择 3 项之一:

  1. 编译错误,让程序员自己决定
  2. 首选拆箱构造函数
  3. 首选转发构造函数

我真的认为这根本不是什么大问题,但我不会从这里开始讨论。

【讨论】:

  • 作为该提案的共同作者,我可以确认这是围绕该功能的最大争议。您在 TS(或当时的标准)中没有它的原因更多是政治性的,而不是技术性的。有争议部分的提案有被拒绝的趋势,所以最好不要提出它。理由是,没有这种转换的 std::optional 比没有 std::optional 好。
  • @Andrzej:您是否碰巧有一个指向您想到要提议并放弃的转换代码的链接?
  • 没有。我无法决定(极端案例的)语义,所以我从来没有达到需要标准的阶段。上面的链接 (groups.google.com/a/isocpp.org/d/topic/std-proposals/…) 包含讨论的变体和一个变体的签名。如果我现在要添加这个转换,我会做 std::tuple 所做的事情。
  • @Andrzej:你的意思是 std::tuple 对 w.r.t 做了什么。使用与元组元素不同类型的单个元素构造?
  • 是的。也就是说,(1)更喜欢拆箱,(2)有条件地显式。
【解决方案3】:

n3672 提案与Boost.Optional 进行比较。主要区别之一是std::optional 不允许从optional&lt;U&gt; 转换为optional&lt;T&gt;std::optional 大量模仿 Boost.Optional(至少 1.48.0,不确定从那以后发生了什么变化)。

ISO C++ Standard - Future Proposals 邮件列表上的讨论讨论了他们为什么遗漏(删除)该功能。 警告:长

【讨论】:

    猜你喜欢
    • 2018-03-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-02-26
    • 1970-01-01
    • 2018-02-10
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多