1

これがばかげた質問かどうかわからないので、もしそうなら私を撃ってください!

私はよく遭遇するこの「ジレンマ」を抱えています。C++ でオーバーロードされた関数を 2 つ言いました。

Fのこの 2 つのオーバーロードがあるとします (以下の疑似コードのみ)。

void F(A a, .../*some other parameters*/)
{ 
  //some code part
  //loop Starts here
  G1(a,.../* some other parameter*/)
  //loop ends here
  //some code part
}


void F(B b, .../*some other parameters*/)
{
  //some code part
  //loop Starts here
  G2(b,.../* some other parameter*/)
  //loop ends here
  //some code part
}

ここで、ABは異なる型であり、G1G2は異なることを行う異なる関数です。G1行とG2行を除くオーバーロードのコード部分は同じであり、非常に長くて広範囲に及ぶ場合があります。問題は、コードをより効率的に記述するにはどうすればよいかということです。当然のことながら、コードを繰り返さないようにしたいと考えています (単純なコピー ペースト ルーチンであるため、簡単に実行できる場合でも)。友人がマクロを提案しました...しかし、それは汚く見えます。これは簡単なことですか。なぜなら、私が今知るのは非常にばかげているからです。提案/ヘルプをいただければ幸いです。

編集:コード例が必要な方には申し訳ありません。コードをより短く/よりきれいにする方法を自問するさまざまな「同様の」状況に遭遇したため、質問は本当に抽象的なものでした。ほとんどの場合、コードは長いので、そもそもこれを尋ねることはありません。KilianDS が指摘したように、関数自体があまり長くないことを確認することも良いことです。しかし、これは避けられない場合もあります。私がこれに遭遇する多くの場合、ループはネストされていて(つまり、相互にいくつかのループがあります)、Fの開始はループの開始であり、Fの終了はそのループを終了します。

ホセ

4

2 に答える 2

7

この場合のコードの重複を防ぐ簡単な方法は、テンプレートを使用することです。例えば:

void G(A a,.../* some other parameter*/)
{
    G1(a,.../* some other parameter*/);
}
void G(B b,.../* some other parameter*/)
{
    G2(b,.../* some other parameter*/);
}

template <typename T>
void F(T x, .../*some other parameters*/)
{ 
  //some code part
  //loop Starts here
  G(x,.../* some other parameter*/)
  //loop ends here
  //some code part
}

またはGを呼び出すかどうかを決定するためにオーバーロードされた関数がどのように使用されるかに注意してください。ただし、これはコンパイルされた実行可能ファイルの重複ではなく、コードの重複を防ぐだけであることに注意してください(テンプレートのインスタンス化ごとに独自のコードが作成されるため)。G1G2

周囲のアーキテクチャに応じて、他にも実行可能なオプションが多数ある場合があります (たとえば、G1/G2 呼び出しの代わりに仮想メソッド、関数ポインター、C++11 を使用している場合はラムダ関数など)。

于 2012-10-23T09:42:18.570 に答える
3

最も明白な解決策は、共通のコード部分を別々の関数に入れ、それらを呼び出すことです:

void F( A a, ... )
{
    commonPrefix(...);
    G1( a, ... );
    commonPostfix(...);
}

void F( B b, ... )
{
    commonPrefix(...);
    G2( a, ... );
    commonPostfix(...);
}

接頭辞と接尾辞の間で共有されるデータが多い場合は、それを保持するクラスを作成し、関数をメンバーにすることができます。

または、おそらく特性を使用して、テンプレートに転送することもできます。

template <typename T>
class GTraits;

template<>
class GTraits<A>
{
    static void doG( A a, ... ) { G1( a, ... ); }
};

template <>
class GTraits<B>
{
    static void doG( B b, ... ) { G2( b, ... ): }
};

template <typename T>
void doF( T t, ... )
{
    //  ...
    GTraits<T>::doG( t, ... );
    //  ...
}

void F(A a, ...)
{
    doF( a, ... );
}

void F(B b, ...)
{
    doF( b, ... );
}

ただし、これにより、コードの共通部分が簡単に複製される可能性があります。(これが問題かどうかは状況によって異なります。ほとんどの場合、コードの肥大化は最小限に抑えられると思います。)

編集:

共通コードにはループ ロジックが含まれていると言うので、次のようなテンプレート メソッド パターンを使用できます。

class CommonBase
{
    virtual void doG( other_params ) = 0;
public:
    void doF()
    {
        //  prefix
        //  loop start
        doG( other_params );
        //  loop end
        //  suffix
    }
};

次に、各関数で個別の派生クラスを定義します。

void F( A a ,... )
{
    class DoG : public CommonBase
    {
        A myA;
        void doG( other_params ) { G1( myA, other_params ); }
    public:
        DoG( A a ) : myA( a ) {}
    } do( a );
    do.doF();
}

転送コンストラクターが必要なため、少し冗長になりますが、共通コードはすべて共通に保たれます。

于 2012-10-23T10:12:25.233 に答える