boost::optional not letting me reassign const value types

让人想犯罪 __ 提交于 2019-12-05 08:20:05

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.

HostileFork

(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

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.

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