3

係数の数値型であるテンプレートPolynom<T>クラスを作成しています。T

多項式の係数は に格納されますstd::vector<T> coefficients。ここで、実多項式にcoefficients[i]対応します。x^i(したがって、x の累乗は昇順です)。

coefficientsvector には常に少なくとも 1 つの要素が含まれていることが保証されています。- ゼロ多項式の場合はT()です。

次のことを行うためにオーバーロードしたいoperator[]

  1. operator[] に渡されるインデックスは、係数を変更/読み取りたい X の累乗に対応します。
  2. ユーザーが係数を読み取るcoefficients.at(i)だけの場合は、負のインデックスに対してスローし、格納された範囲内のインデックスに対して返さなければなりません。また、スローではなく、他のすべてのインデックスに対して適切に 0 を返す必要があります。
  3. ユーザーが係数を変更したい場合は、負のインデックスをスローする必要がありますが、指定されたインデックスが より大きいか等しい場合でも、ユーザーは他のすべてのインデックスを自由に変更できますcoefficients.size()。そのため、何らかの方法でベクトルのサイズを変更したいと考えています。

私が衝突した主な問題は次のとおりです。

1.

読み取りケースと書き込みケースを区別するにはどうすればよいですか? ある人は説明なしで私を残しましたが、2つのバージョンを書いていると言いました:

const T& operator[] (int index) const;
T& operator[] (int index);

不十分でした。ただし、読み取りの場合、コンパイラは const バージョンを好むと思いましたね。

2.

coefficients末尾のゼロがベクトルに格納されないようにしたいと思います。したがって、係数のミュータブルを返す「前に」、T&ユーザーが割り当てたい値を事前に知る必要があります。operator[]そして、それが2番目の引数を受け取らないことを私は知っています。

明らかに、この値がゼロでない (T() ではない) 場合、ベクトルのサイズを変更し、渡された値に適切な係数を設定する必要があります。

T&ただし、事前に ( from を返す前にoperator[]) 行うことはできません。割り当てられる値が T() である場合、事前に係数ベクトルのサイズを変更すると、最終的に多くの末尾の「ゼロ」が発生するためです。

もちろん、クラスの他のすべての関数で末尾のゼロをチェックし、その場合は削除できます。私には非常に奇妙な決定のように思えます.サイズ> 1の場合、ベクトルの最後にゼロがないことを前提として、すべての関数が機能し始めることを望みます.

この問題の具体的な解決策を教えてください。T&オーバーロードされた に暗黙的に変換可能な内部クラスを作成することについて何か聞いたことoperator=がありますが、詳細が不足しています。

事前にどうもありがとうございました!

4

5 に答える 5

4

あなたが試すことができる1つのオプション(私はこれをテストしていません):

template<typename T>
class MyRef{
private:
   int index;
   Polynom<T>*p;
public:
    MyRef(int index, Polynom<T>*p) : index(index), p(p) { }

    MyRef<T>& operator=(T const&t); //and define these appropriately
    T operator T() const;         
};

次のように定義します。

    MyRef<T> operator[](int index){
        return MyRef<T>(index, this);
    }

このようにして、値を「参照」に割り当てると、多項式で必要なすべてのデータにアクセスでき、適切なアクションが実行されます。

私はあなたの実装に十分に精通していないので、代わりに、次のように機能する非常に単純な動的配列の例を示します。

  • int index気にせずに読むことができます。以前に書き込まれていない要素は、次のように読み取られる必要があります0
  • 現在割り当てられている配列の末尾を超えて要素に書き込むと、その要素は再割り当てされ、新しく割り当てられた要素は に初期化され0ます。
#include <cstdlib>
#include <iostream>
using namespace std;

template<typename T>
class my_array{
private:
    T* _data;
    int _size;

    class my_ref{

        private:
            int index;
            T*& obj;
            int&size;
        public:
            my_ref(T*& obj, int&size, int index)
                : index(index), obj(obj), size(size){}

            my_ref& operator=(T const& t){

                if (index>=size){    
                    obj = (T*)realloc(obj, sizeof(T)*(index+1) );
                    while (size<=index)
                        obj[size++]=0;
                }
                obj[index] = t;

                return *this;
            }

            //edit:this one should allow writing, say, v[1]=v[2]=v[3]=4;
            my_ref& operator=(const my_ref&r){              
                operator=( (T) r);
                return *this;
            }

            operator T() const{
                return (index>=size)?0:obj[index];
            }

    };

public:
    my_array() : _data(NULL), _size(0) {}

    my_ref operator[](int index){
        return my_ref(_data,_size,index);
    }

    int size() const{ return _size; }

};

int main(){

    my_array<int> v;

    v[0] = 42;
    v[1] = 51;
    v[5] = 5; v[5]=6;
    v[30] = 18;

    v[2] = v[1]+v[5];
    v[4] = v[8]+v[1048576]+v[5]+1000;

    cout << "allocated elements: " <<  v.size() << endl;
    for (int i=0;i<31;i++)
        cout << v[i] << " " << endl;

    return 0;
}

これは非常に単純な例であり、現在の形式ではあまり効率的ではありませんが、ポイントを証明する必要があります。

最終的には、適切に動作するoperator&ようなものを許可するために、オーバーロードすることが必要になる場合があります。*(&v[0] + 5) = 42;この例では、その フィールドで算術演算を実行し、 new を返すように定義する をoperator&与えることができます。最後に、オーバーロードして a に戻ることができます。my_pointeroperator+indexmy_pointeroperator*()my_ref

于 2011-11-09T09:26:29.937 に答える
2

これに対する解決策は、プロキシ クラスです (テストされていないコードが続きます)。

template<typename T> class Polynom
{
public:
   class IndexProxy;
   friend class IndexProxy;
   IndexProxy operator[](int);
   T operator[](int) const;
   // ...
private:
   std::vector<T> coefficients;
};

template<typename T> class Polynom<T>::IndexProxy
{
public:
  friend class Polynom<T>;
  // contrary to convention this assignment does not return an lvalue,
  // in order to be able to avoid extending the vector on assignment of 0.0
  T operator=(T const& t)
  {
    if (theIndex >= thePolynom.coefficients.size())
      thePolynom.coefficients.resize(theIndex+1);
    thePolynom.coefficients[theIndex] = t;
    // the assignment might have made the polynom shorter
    // by assigning 0 to the top-most coefficient
    while (thePolynom.coefficients.back() == T())
      thePolynom.coefficients.pop_back();
    return t;
  }
  operator T() const
  {
    if (theIndex >= thePolynom.coefficients.size())
      return 0;
    return thePolynom.coefficients[theIndex];
  }
private:
  IndexProxy(Polynom<T>& p, int i): thePolynom(p), theIndex(i) {}
  Polynom<T>& thePolynom;
  int theIndex;
}

template<typename T>
  Polynom<T>::IndexProxy operator[](int i)
  {
    if (i < 0) throw whatever;
    return IndexProxy(*this, i);
  }

template<typename T>
  T operator[](int i)
{
  if (i<0) throw whatever;
  if (i >= coefficients.size()) return T();
  return coefficients[i];
}

上記のコードは明らかに最適化されていません (特に代入演算子には明らかに最適化の余地があります)。

于 2011-11-09T09:47:29.620 に答える
1

演算子のオーバーロードでは、読み取りと書き込みを区別できません。あなたができる最善のことは、コードスニペットが行うことであるconst設定と非設定での使用を区別することです。constそう:

Polynomial &poly = ...;

poly[i] = 10;  // Calls non-const version
int x = poly[i];  // Calls non-const version

const Polynomial &poly = ...;

poly[i] = 10;   // Compiler error!
int x = poly[i]  // Calls const version

したがって、両方の質問に対する答えのように思えます。したがって、機能を分離するsetことgetです。

于 2011-11-09T09:27:07.363 に答える
1

あなたの問題に対する2つの解決策があります:

  1. 係数を に格納する代わりに、 に格納しstd::vector<T>ますstd::map<unsigned int, T>。このようにして、ゼロ以外の係数のみを保存します。std::map格納されているゼロを消費する独自の コンテナを作成できます。このようにして、n が大きい x^n 形式の多項式のストレージも節約できます。

  2. インデックス (パワー) と係数値を格納する内部クラスを追加します。からこの内部クラスのインスタンスへの参照を返しますoperator[]。内部クラスは上書きしますoperator=。オーバーライドoperator=では、内部クラス インスタンスに格納されているインデックス (電力) と係数を取得し、それらをstd::vector係数を格納する場所にフラッシュします。

于 2011-11-09T09:34:47.250 に答える
0

これは不可能です。私が考えることができる唯一の方法は、新しい係数を追加するための特別なメンバー関数を提供することです。

コンパイラは、戻り値に対して実行される操作の種類をチェックするのではなく、の型を調べることによって、 バージョンconstと非バージョンを決定します。constPolynom

于 2011-11-09T09:26:20.250 に答える