9

この不自然な例を考えると:

struct point_2d {
  point_2d& x( int n ) {
    x_ = n;
    return *this;
  }

  point_2d& y( int n ) {
    y_ = n;
    return *this;
  }

  int x_, y_;
};

struct point_3d : point_2d {
  point_3d& z( int n ) {
    z_ = n;
    return *this;
  }

  int z_;
};

int main() {
  point_3d p;
  p.x(0).y(0).z(0); // error: "point_2d" has no member named "z"
  return 0;
}

アイデアは、「メンバー関数チェーン」を使用して、連続して複数のメンバー関数を呼び出すことができるようにすることです。(これには多くの例があります。上記は、この質問をするために私が考えることができる最短の例です。私の実際の問題は同様であり、以下に説明されています。)

問題は、派生クラスが独自の連鎖メンバー関数を追加するが、最初に基本クラスのメンバー関数を呼び出すと、派生クラスのメンバー関数を呼び出すためにはもちろん機能しない基本クラス参照を取得することです。

この問題を解決し、メンバーと関数の連鎖を実行する機能を維持するための賢い方法はありますか?


実際の問題

の実際の問題は、私の基本クラスが例外であり、私の派生クラスが基本例外から派生したクラスであるということです。これらのクラスについても、メンバー関数チェーンを使用したいと思います。

class base_exception : public std::exception {
  // ...
  base_exception& set_something( int some_param ) {
    // ...
    return *this;
  }
};

class derived_exception : public base_exception {
  // ...
};

int main() {
  try {
    // ...
    if ( disaster )
      throw derived_exception( required_arg1, required_arg2 )
            .set_something( optional_param );
  }
  catch ( derived_exception const &e ) {
    // terminate called after throwing an instance of 'base_exception'
  }
}

問題はそれset_something()が戻ることですbase_exceptionが、catch期待しderived_exceptionます。もちろん、人間は例外の実際のタイプがであると言うことができますderived_exceptionが、コンパイラは明らかにわかりません。

これが私が本当に解決しようとしている問題です。つまり、基本例外クラスが例外オブジェクトにオプションのパラメーターを設定できるようにする一方で、派生型のインスタンスを返す方法です。point_2d私が上で示した例は、人々が理解できる同じ問題のより小さくて単純なバージョンであり、より小さな問題の解決策も私の実際の問題を解決するだろうと私は信じています。

base_exceptionテンプレートを作成して、次のような派生型を渡すことを検討したことに注意してください。

template<class Derived>
class base_exception {
  // ...
  Derived& set_something( int some_param ) {
    // ...
    return *this;
  }
};

more_derived_exception実際には問題は解決すると思いますが、別のクラスがから派生した場合derived_exception、同じ問題に戻るため、完全な解決策ではありません。

4

3 に答える 3

7

あなたが探しているのは、この StackOverflow answerからコピーしているNamed Parameter Idiomです。実際のオブジェクトへの参照を返すのではなく、特別なパラメーター オブジェクトへの参照を返し、例外オブジェクトのコンストラクターに依存して、すべてのパラメーターが入力されたら暗黙的な変換を行います。これは実に巧妙です。

于 2011-02-16T18:15:00.413 に答える
1

こんにちは、私はちょうど同様の問題を抱えていました。ここに私の解決策があります:

template<class DerivedOptions>
class SomeOptions
{
  private:
    DerivedOptions* derived;
    int param1_;
  public:
    SomeOptions()
    {
        derived = reinterpret_cast<DerivedOptions*>(this);
    }

    DerivedOptions & set_some_options(int param1)
    {
        param1_ = param1;
        return *derived;
    }
};

struct MoreOptions: public SomeOptions<MoreOptions>
{
  private:
    int more_;
  public:
    MoreOptions & set_more_options(int more)
    {
        more_ = more;
        return *this;
    }
};

私が何をしているか foo を知っているものは間違いなく含まれていますが、一方で (少なくとも私のアプリケーションでは) Base クラスは継承なしで使用することを意図していません。

よろしく、 レギ

于 2011-05-24T13:35:21.063 に答える
0

最も単純なアプローチを採用しないのはなぜですか (おそらく最もエレガントではないかもしれません):

if ( disaster )
{
    derived_exception e = derived_exception( required_arg1, required_arg2 );
    e.set_something( optional_param );
    throw e;
}

それはあなたの問題を解決しませんか、それとも何か見逃していますか?

于 2011-02-16T17:46:17.977 に答える