boost :: optional不让我重新分配const值类型

时间:2012-07-12 19:31:37

标签: c++ boost assignment-operator const-correctness boost-optional

在我看来,应该有boost::optional

的四种变体
  • optional<Foo> =&gt;持有一个可变的Foo,可以在初始化后重新分配

  • optional<Foo const> const =&gt;持有const Foo,初始化后无法重新分配

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

  • optional<Foo const> =&gt; (应该?)持有一个const Foo,可以在初始化后重新分配

前2个案例按预期工作。但optional<Foo> const取消引用const Foo,optional<Foo const>不允许在初始化后重新分配(如this question中所述)。

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

  

/usr/include/boost/optional/optional.hpp:486:错误:将'const Foo'作为'Foo&amp;的'this'参数传递Foo :: operator =(const Foo&amp;)'丢弃限定符[-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源(我已经做了它来做support movable types,但我怀疑他们会自己这样做那么什么微创改变可以做到这一点?

3 个答案:

答案 0 :(得分:3)

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

  

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

也就是说,假设您有boost::optional<>。由于默认构造的theFoo=defaultFoo; 为空,因此语句为:

defaultFoo

应该是指“将构造theFoo复制到const Foo的内部存储中”。由于内部存储中没有任何内容,这是有道理的,即使内部存储应该容纳theFoo。完成后,theFoo将不会为空。

theFoo=defaultFoo; 包含值后,语句

defaultFoo

应表示“将theFoo分配到theFoo内部存储空间中的对象”。但const的内部存储不可分配(因为它是boost::optional<...>),因此这会引发(编译时?)错误。

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


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

  

如果*已初始化,则首先销毁其当前内容。然后使用T的拷贝构造函数。

毕竟,通过说*theFoo = rhs,完全有可能调用{{1}}的赋值运算符(如果这是你真正想做的)。

答案 1 :(得分:2)

回答你的三个问题:

  1. 实现遵循设计理念,即可选的赋值使用T的赋值;从这个意义上讲,实施很好。
  2. 可选项的设计考虑了可能的扩展。可选只是底层类optional_base的接口。您可以从optional_base派生自己的类,而不是使用可选项。 optional_base有一个受保护的成员构造,几乎可以满足您的需求。你需要一个新成员,比如reset(T),第一次清除optional_base,然后调用construct()。
  3. 或者,您可以将成员重置(T)添加到可选项。这将是最不具侵入性的变化。
  4. 您还可以尝试this proposal中的可选参考实现。

答案 2 :(得分:2)

(1)一个人对“应该”行为的看法取决于选项是“零或一个任意类型对象的容器”或< em>“一个类型的瘦代理,具有附加功能”。现有代码使用后一种思想,通过这样做,它删除了列表中“四种不同行为”的一半。这降低了复杂性,并使您无意中引入低效的用法。

(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)我们必须跟进information given by @Andrzej才能找到答案。但是这样的实现更改可能不会比每次创建新的可选项更好(如上面的循环中所示)。最好接受现有的设计。


来源:@R.MartinhoFernandez@KerrekSB