8

の4つのバリエーションがあるべきだと私には思えますboost::optional

  • optional<Foo>=> 変更可能な Foo を保持し、初期化後に再割り当てできます

  • optional<Foo const> const=> const Foo を保持し、初期化後に再割り当てできません

  • optional<Foo> const=> (すべき?) 変更可能な Foo を保持しますが、初期化後に再割り当てすることはできません

  • optional<Foo const>=> (すべき?) const Foo を保持し、初期化後に再割り当てできます

最初の 2 つのケースは期待どおりに機能します。ただし、optional<Foo> constconst Foo への逆参照、および初期化後の再割り当ては許可されません (この質問optional<Foo const>で触れたように)。

const 値型の再割り当ては、特に私が遭遇したものであり、エラーは次のとおりです。

/usr/include/boost/optional/optional.hpp:486: エラー: 'Foo& Foo::operator=(const Foo&)' の 'this' 引数として 'const Foo' を渡すと修飾子が破棄されます [-fpermissive]

そして、それはここで起こります:

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

構築後、実装は、optional をパラメーター化した型の代入演算子を使用します。明らかに、定数値である左側のオペランドは必要ありません。しかし、次の場合のように、非 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::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;

「内部ストレージdefaultFooのオブジェクトに割り当てる」ことを意味する必要があります。theFooしかし、theFoos 内部ストレージは (そのままではconst) 割り当て可能ではないため、(コンパイル時?) エラーが発生するはずです。

残念ながら、最後の 2 つのステートメントは同一であることに気付くでしょうが、概念的にはコンパイル時の動作が異なります。ただし、コンパイラに 2 つの違いを伝えるものは何もありません。


特にあなたが説明しているシナリオでは、boost::optional<...>代わりにセマンティクスを持つように の代入演算子を定義する方が理にかなっているかもしれません:

*this が初期化されている場合、その現在の内容が最初に破棄されます。次に、Tのコピー コンストラクターが使用されます。

結局のところ、それTが本当にやりたいことであれば、 と言って の代入演算子を呼び出すことは完全に可能です*theFoo = rhs

于 2012-07-12T20:24:12.430 に答える
2

3 つの質問に答えるには:

  1. 実装は、オプションの割り当てが T の割り当てを使用するという設計哲学に従います。この意味で、実装は問題ありません。
  2. optional は可能な拡張を念頭に置いて設計されました。Optional は、基になるクラス optional_base の単なるインターフェイスです。optional を使用する代わりに、optional_base から独自のクラスを派生させることができます。optional_base には保護されたメンバー構成があり、必要なことはほとんど実行されます。最初にoptional_baseをクリアしてからconstruct()を呼び出すreset(T)など、新しいメンバーが必要になります。
  3. または、メンバ reset(T) をオプションに追加できます。これは、最も邪魔にならない変更です。

この提案から optional の参照実装を試すこともできます。

于 2012-07-12T21:46:57.107 に答える
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

于 2012-07-14T00:24:03.200 に答える