4

この質問は、 Visitorのようなパターンを使用して再帰 (検索) アルゴリズムをカスタマイズする Boost.Graph ライブラリ (BGL) に大まかに基づいています。BGL はビジター オブジェクトを (STL 関数オブジェクトと同様に) 値で渡し、ドキュメントの状態

ビジター パラメータは値で渡されるため、ビジターに状態が含まれている場合、アルゴリズム中の状態の変更は、渡されたビジター オブジェクトではなく、ビジター オブジェクトのコピーに対して行われます。したがって、ビジターにこれを保持させたい場合があります。ポインタまたは参照による状態。

私の質問: ステートフル ビジター クラスの参照セマンティクスを実装する最良の方法は何ですか? 正確なポインター クラス (raw vs unique vs shared、const vs non-const) から抽象化すると、参照を配置するのに最適な場所は次のどれですか: パラメーターの受け渡しまたはデータ メンバーでしょうか?

代替案 1 : ビジターはポインターによって状態を保持し、値によって渡されます (Boost.Graph のように)

class Visitor
{
public:
    Visitor(): state_(new State()) {}
    void start() { /* bla */ }
    void finish() { /* mwa */ }
private:
    State* state_;
}

template<typename Node, typename Visitor>
int algorithm(Node const& n, Visitor v)
{
    v.start();
    algorithm(next(n), v);
    v.finish();
}

選択肢 2 : ビジターはデータを値で保持し、ポインターで渡されます

class Visitor
{
public:
    Visitor(): state_() {}
    void start() { /* bla */ } 
    void finish() { /* mwa */ }
private:
    State state_;
}

template<typename Node, typename Visitor>
int algorithm(Node const& n, Visitor* v)
{
    v->start();
    algorithm(next(n), v);
    v->finish();
}

私の現在の傾向:選択肢 1 [ポインター/参照を保持するオブジェクトの値渡し] は、ビジターが値のセマンティクスを満たさないため、少し不快だと思うので、パラメーター リストで参照のセマンティクスを明確にしたいと思います [代替 2] . ここで関連する他の考慮事項や代替手段はありますか?

4

3 に答える 3

2

3 番目の選択肢があります。

class Visitor
{
public:
    Visitor(): state_() {}
    void start() { /* bla */ } 
    void finish() { /* mwa */ }
private:
    State state_;
};

template<typename Node, typename Visitor>
int algorithm(Node const& n, Visitor v)
{
    v.start();
    algorithm(next(n), v);
    v.finish();
}

// Set the reference semantics here, use value everywhere else
algorithm(myNode, boost::ref(myVisitor)); // ... or std::ref in c++11

これは通常、何かをポインターまたは参照として明示的にマークするのではなく、標準で好まれていると思います。結局、この問題を解決するためstd::refstd::cref導入されました。

一方、「C++ Coding Standards」という本の中で、Sutter と Alexandrescu は、ファンクターは常に簡単かつ迅速にコピーできるようにすべきだと主張しています。内部で参照カウント状態ブロックを使用することをお勧めします (IIRC、ここに本はありません)。そのため、問題を解決したり解決しstd::refたりする間std::cref、それらはより一般的に非ファンクターオブジェクトを「適応」させるために使用されstd::vectorますstd::bind

代替案 1 shared_ptr<T>(またはもっと良いのshared_ptr<T const>は ) がおそらく最良の選択肢です。どちらの場合でも、BGL コードの値セマンティクスの背後にポインター セマンティクスを「ラップ」しているだけです。これは、すべてのオブジェクトの有効期間が適切である限り問題ありません。

于 2013-01-21T14:55:53.523 に答える
1

代替案 1 に対するあなたの不快感は理解できますが、これは「そのバスが出発した」場合だと思います。言い換えれば、C++ 標準ライブラリ (および BGL だけでなく Boost) の方向性は、「保持された参照」パターンの使用を支持しています。

たとえば、ラムダ式で実装できるファンクタの広範な使用を考えてみましょう。私の知る限り、すべての標準ライブラリ (およびブースト) インターフェースは、ファンクター引数を値渡しするため、ファンクターが状態を保持する場合は、参照によって保持する必要があります。[&](){}したがって、私たちは見ることよりも見ることに慣れるべきです[=](){}。そして類推すると、ビジターが自分の状態への参照 (またはポインターですが、私は参照を好みます) を保持しているのを見るのに慣れる必要があります。

ファンクター (およびビジター) を参照ではなく値で渡すのには、実際には十分な理由があります。それらが参照によって渡された場合、const&状態の変更を不可能にする 、または&一時的な値の使用を不可能にする によって渡す必要があります。他の唯一の可能性は明示的なポインターを渡すことですが、ラムダまたは一時値では使用できません (一時値が不必要にヒープに割り当てられていない限り)。

于 2013-01-21T14:45:30.963 に答える
0

Visitor に状態があるのにステートレスにしようとするのは無意味です。(2)で問題ないと思います。

于 2013-01-21T14:45:15.563 に答える