boost::optional not letting me reassign const value types

大兔子大兔子 提交于 2019-12-10 04:32:21

问题


It seems to me there should be four variants of boost::optional

  • optional<Foo> => holds a mutable Foo and can be reassigned after initialization

  • optional<Foo const> const => holds a const Foo and can't be reassigned after initialization

  • optional<Foo> const => (should?) hold a mutable Foo but can't be reassigned after initialization

  • optional<Foo const> => (should?) hold a const Foo and can be reassigned after initialization

The first 2 cases work as expected. But the optional<Foo> const dereferences to a const Foo, and the optional<Foo const> doesn't allow reassignment after initialization (as touched upon in this question).

The reassignment of the const value types is specifically what I ran into, and the error is:

/usr/include/boost/optional/optional.hpp:486: error: passing ‘const Foo’ as ‘this’ argument of ‘Foo& Foo::operator=(const Foo&)’ discards qualifiers [-fpermissive]

And it happens here:

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

After construction, the implementation uses the assignment operator for the type you parameterized the optional with. It obviously doesn't want a left-hand operand which is a const value. But why shouldn't you be able to reset a non-const optional to a new const value, such as in this case:

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();
}

Some Questions:

  • Am I right that wanting this is conceptually fine, and not being able to do it is just a fluke in the implementation?

  • If I don't edit the boost sources, what would be a clean way to implement logic like in the loop above without scrapping boost::optional altogether?

  • If this does make sense and I were to edit the boost::optional source (which I've already had to do to make it support movable types, though I suspect they'll be doing that themselves soon) then what minimally invasive changes might do the trick?


回答1:


So basically the problem seems to be related to this note in the documentation for optional& optional<T (not a ref)>::operator= ( T const& rhs ):

Notes: If *this was initialized, T's assignment operator is used, otherwise, its copy-constructor is used.

That is, suppose you have boost::optional<const Foo> theFoo;. Since a default constructed boost::optional<> is empty, the statement:

theFoo=defaultFoo;

should mean "copy construct defaultFoo into theFoos internal storage." Since there's nothing already in that internal storage, this makes sense, even if the internal storage is supposed to house a const Foo. Once finished, theFoo will not be empty.

Once theFoo contains a value, the statement

theFoo=defaultFoo;

should mean "assign defaultFoo into the object in theFoos internal storage." But theFoos internal storage isn't assignable (as it is const), and so this should raise a (compile time?) error.

Unfortunately, you'll notice the last two statements are identical, but conceptually require different compile time behavior. There's nothing to let the compiler tell the difference between the two, though.


Particularly in the scenario you're describing, it might make more sense to define boost::optional<...>'s assignment operator to instead have the semantics:

If *this was initialized, its current contents are first destroyed. Then T's copy-constructor is used.

After all, it's entirely possible to invoke T's assignment operator if that's what you really want to do, by saying *theFoo = rhs.




回答2:


(1) One's opinion on what the behavior "should" be depends on whether optionals are "a container for zero or one objects of an arbitrary type" or "a thin proxy for a type, with an added feature". The existing code uses the latter idea, and by doing so, it removes half of the "four different behaviors" in the list. This reduces the complexity, and keeps you from unintentionally introducing inefficient usages.

(2) For any Foo type whose values are copyable, one can easily switch between mutable and immutable optionals by making a new one. So in the given case, you'd get it as mutable briefly and then copy it into an immutable value.

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();
}

Given the model that it's a "thin proxy" for a type, this is exactly the same kind of thing you would have to do if the type were not wrapped in an optional. An ordinary const value type needs the same treatment in such situations.

(3) One would have to follow up on the information given by @Andrzej to find out. But such an implementation change would probably not perform better than creating a new optional every time (as in the loop above). It's probably best to accept the existing design.


Sources: @R.MartinhoFernandez, @KerrekSB




回答3:


To answer your three questions:

  1. The implementation follows the design philosophy that optional's assignment uses T's assignment; and in this sense, the implementation is fine.
  2. optional was designed with possible extensions in mind. Optional is only an interface for the underlying class optional_base. Instead of using optional you can derive your own class from optional_base. optional_base has a protected member construct, which does almost what you need. You will need a new member, say reset(T) that firsts clears the optional_base and then calls construct().
  3. Alternatively, you can add member reset(T) to optional. This would be the least intrusive change.

You could also try the reference implementation of optional from this proposal.



来源:https://stackoverflow.com/questions/11459270/boostoptional-not-letting-me-reassign-const-value-types

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!