8

在我看来应该有四种变体boost::optional

  • optional<Foo>=> 持有一个可变的 Foo 并且可以在初始化后重新分配

  • optional<Foo const> const=> 持有一个 const Foo 并且在初始化后不能重新分配

  • optional<Foo> const=>(应该?)持有一个可变的 Foo 但在初始化后不能重新分配

  • optional<Foo const>=> (should?) 持有一个 const Foo 并且可以在初始化后重新分配

前 2 个案例按预期工作。但是对optional<Foo> constconst Foo 的取消引用,并且optional<Foo const>不允许在初始化后重新分配(如本问题所述)。

const 值类型的重新分配具体是我遇到的,错误是:

/usr/include/boost/optional/optional.hpp:486:错误:将 'const Foo' 作为 'Foo& Foo::operator=(const Foo&)' 的 'this' 参数传递会丢弃限定符 [-fpermissive]

它发生在这里:

void assign_value(argument_type val,is_not_reference_tag) { get_impl() = val; }

构造后,实现将赋值运算符用于您参数化可选项的类型。它显然不想要一个 const 值的左操作数。但是为什么不能将非 const 选项重置为新的 const 值,例如在这种情况下:

optional<Foo const> theFoo (maybeGetFoo());
while (someCondition) {

    // do some work involving calling some methods on theFoo
    // ...but they should only be const ones

    theFoo = maybeGetFoo();
}

一些问题:

  • 想要这个在概念上是好的,而不能做到这一点只是实施中的侥幸,我对吗?

  • 如果我不编辑 boost 源,那么在不完全废弃 boost::optional 的情况下实现上述循环中的逻辑的干净方法是什么?

  • 如果这确实有意义并且我要编辑 boost::optional 源(我已经不得不这样做以使其支持可移动类型,尽管我怀疑他们很快就会自己这样做)那么微创更改可能做这个把戏?

4

3 回答 3

3

所以基本上这个问题似乎与文档中的这个注释有关optional& optional<T (not a ref)>::operator= ( T const& rhs )

注意:如果 *this 已初始化,则使用 T 的赋值运算符,否则使用其复制构造函数。

也就是说,假设你有boost::optional<const Foo> theFoo;. 由于默认构造boost::optional<>为空,因此语句:

theFoo=defaultFoo;

应该意味着“将构造复制defaultFootheFoo内部存储中”。由于该内部存储中已经没有任何内容,因此这是有道理的,即使内部存储应该容纳const Foo. 一旦完成,theFoo就不会为空。

一旦theFoo包含一个值,语句

theFoo=defaultFoo;

应该意味着“分配defaultFootheFoo内部存储中的对象”。但是theFoos 内部存储是不可分配的(因为它是const),所以这应该会引发一个(编译时间?)错误。

不幸的是,您会注意到最后两个语句是相同的,但在概念上需要不同的编译时行为。但是,没有什么可以让编译器分辨出两者之间的区别。


特别是在您描述的场景中,将boost::optional<...>' 赋值运算符定义为具有语义可能更有意义:

如果 *this 被初始化,它的当前内容首先被销毁。然后T使用 的复制构造函数。

毕竟,T如果这是您真正想要做的事情,完全可以调用 ' 赋值运算符,只需说*theFoo = rhs.

于 2012-07-12T20:24:12.430 回答
2

回答你的三个问题:

  1. 实现遵循 optional 的赋值使用 T 的赋值的设计理念;从这个意义上说,实施很好。
  2. optional 的设计考虑了可能的扩展。Optional 只是底层类 optional_base 的接口。您可以从 optional_base 派生自己的类,而不是使用 optional。optional_base 有一个受保护的成员结构,它几乎可以满足您的需要。您将需要一个新成员,例如 reset(T),它首先清除 optional_base,然后调用construct()。
  3. 或者,您可以将成员 reset(T) 添加到可选。这将是侵入性最小的更改。

你也可以试试这个提案中 optional 的参考实现。

于 2012-07-12T21:46:57.107 回答
2

(1)对“应该”行为的看法取决于选项是“零个或一个任意类型的对象的容器”还是“具有附加功能的类型的精简代理”。现有代码采用后一种思路,通过这样做,将列表中的“四种不同行为”去掉了一半。这降低了复杂性,并防止您无意中引入低效的用法。

(2)对于任何Foo值可复制的类型,可以通过创建一个新选项轻松地在可变选项和不可变选项之间切换。所以在给定的情况下,你会简单地将它作为可变的,然后将它复制到一个不可变的值中。

optional<Foo> theMutableFoo (maybeGetFoo());
while (someCondition) {
    optional<Foo const> theFoo (theMutableFoo);

    // do some work involving calling some methods on theFoo
    // ...but they should only be const ones
    // ...therefore, just don't use theMutableFoo in here!

    theMutableFoo = maybeGetFoo();
}

考虑到它是类型的“瘦代理”的模型,如果类型没有包装在可选项中,这与您必须做的事情完全相同。在这种情况下,普通的 const 值类型需要同样的处理。

(3) 必须跟进@Andrzej 提供的信息才能找到答案。但是这样的实现更改可能不会比每次都创建一个新的可选(如上面的循环)更好。最好接受现有的设计。


资料来源:@R.MartinhoFernandez, @ KerrekSB

于 2012-07-14T00:24:03.200 回答