0

いくつかの場所で繰り返されるネストされたif/switchステートメントがたくさんあるプログラムがあります。私はそれを抽出してスイッチをテンプレートメソッドクラスに入れ、クライアントがオーバーロードを使用して具体的に処理したいスイッチブランチをオーバーロードできるようにしました。

class TraitsA {};
class TraitsB : public TraitsA {};

class Foo
{
    bool traitsB;
public:
    // Whether or not a Foo has traitsB is determined at runtime. It is a
    // function of the input to the program and therefore cannot be moved to
    // compile time traits (like the Iterators do)
    Foo() : traitsB(false) {}
    virtual ~Foo() {}
    bool HasTraitsB() const { return traitsB; }
    void SetTraitsB() { traitsB = true; }
};

class SpecificFoo : public Foo
{
};

template <typename Client> //CRTP
class MergeFoo
{
protected:
    Foo DoMerge(Foo&, const Foo&, int, TraitsA)
    {
        // Do things to merge generic Foo
    }
public:
    // Merge is a template method that puts all the nasty switch statements
    // in one place.
    // Specific mergers implement overloads of DoMerge to specify their
    // behavior...
    Foo Merge(Foo* lhs, const Foo* rhs, int operation)
    {
        const Client& thisChild = *static_cast<const Client*>(this);

        SpecificFoo* lhsSpecific = dynamic_cast<SpecificFoo*>(lhs);
        const SpecificFoo* rhsSpecific = dynamic_cast<const SpecificFoo*>(rhs);

        // In the real code these if's are significantly worse
        if (lhsSpecific && rhsSpecific)
        {
            if (lhs->HasTraitsB())
            {
                return thisChild.DoMerge(*lhsSpecific, 
                               *rhsSpecific, 
                               operation,
                               TraitsB());
            }
            else
            {
                return thisChild.DoMerge(*lhsSpecific,
                               *rhsSpecific,
                               operation,
                               TraitsA());
            }
        }
        else
        {
            if (lhs->HasTraitsB())
            {
                return thisChild.DoMerge(*lhs, *rhs, operation, TraitsB());
            }
            else
            {
                return thisChild.DoMerge(*lhs, *rhs, operation, TraitsA());
            }
        }
    }
};

class ClientMergeFoo : public MergeFoo<ClientMergeFoo>
{
    friend class MergeFoo<ClientMergeFoo>;
    Foo DoMerge(SpecificFoo&, const SpecificFoo&, int, TraitsA)
    {
        // Do things for specific foo with traits A or traits B
    }
};

class ClientMergeFooTwo : public MergeFoo<ClientMergeFoo>
{
    friend class MergeFoo<ClientMergeFooTwo>;
    Foo DoMerge(SpecificFoo&, const SpecificFoo&, int, TraitsB)
    {
        // Do things for specific foo with traits B only
    }
    Foo DoMerge(Foo&, const Foo&, int, TraitsA)
    {
        // Do things for specific foo with TraitsA, or for any Foo
    }
};

ただし、これはコンパイルに失敗し(少なくともClientMergeFooTwoの場合)、Foo&をSpecificFoo&に変換できないと言います。で完全に優れた一般的なオーバーロードを選択する代わりに、その変換が失敗する理由はありMergeFooますか?

編集:まあ、この擬似コードの例は、私がそれを書き込もうとした速さを考えると、明らかにうまくいきませんでした。私はいくつかの間違いを訂正しました...

4

4 に答える 4

2

MergeFooで完全に優れた汎用オーバーロードを選択する代わりに、その変換が失敗する理由はありますか?

はい、名前を隠すルールがあるためです。派生クラスの関数が基本クラスの関数と同じ名前である場合、基本クラスの関数は「非表示」になり、関連する関数のパラメーターも調べません。

using MergeFoo::DoMergeとは言うものの、解決策は簡単です。パブリック部分で単純なものを使用して、派生クラスで基本クラスバージョンを使用できるようにします。

于 2011-07-01T01:11:43.330 に答える
0
const thisChild& = *static_cast<const Client*>(this);

分かりませんでしたか?タイプ(または変数)はどこにありますか?あなたはこれを意味しました:

const Client & thisChild = *static_cast<const Client*>(this);

そして、以下で

SpecificFoo* rhsSpecific = dynamic_cast<const SpecificFoo*>(rhs);

忘れたターゲットのように、const-nessに不一致がありますconst

于 2011-06-30T23:17:30.053 に答える
0
class ClientMergeFooTwo : public MergeFoo<ClientMergeFoo>

これが問題の原因である可能性があります。

于 2011-06-30T23:18:23.883 に答える
0

失敗している場所についてもう少し情報を使用できますが、意図したことを実行するには、パブリックマージ関数でDoMerge()を呼び出すのではなく、Client :: DoMerge()を呼び出す必要があるようです。 MergeFooの。

于 2011-06-30T23:20:02.053 に答える