2

私は三重対角行列を実装しており、可能な限り効率的でなければなりません。明らかに、私はデータを含む要素のみを保持します。をオーバーロードoperator()してマトリックスへのインデクサーとして機能させましたが、ユーザーがマトリックスを変更できるように、この演算子が参照を返すようにします。ただし、return 0;ゼロは参照ではないため、非三重対角要素だけを対象にすることはできません。ユーザーに三重対角のデータを変更させるにはどうすればよいですか?しかし、がoperator()非三重対角要素の検査に使用される場合、0への参照ではなく0のみを返しますか?

以下は関連するクラスの定義です

template <class T>
class tridiagonal
{
  public:
    tridiagonal();
    ~tridiagonal();
    T& operator()(int i, int j);
    const T& operator()(int i, int j) const; 

  private:
    //holds data of just the diagonals
    T * m_upper;
    T * m_main;
    T * m_lower;
};
4

4 に答える 4

1

使用できるトリックの1つは、non-const operator()(int、int)メソッドが小さなヘルパーオブジェクトを返すようにすることです。ヘルパーは、マトリックスへの割り当てと値の引き出しを区別するために使用されます。これにより、2つの操作で異なる動作をすることができます。特に、誰かがゼロでなければならない値に割り当てようとした場合にスローすることができます。

このコードは少なくともVC10でコンパイルされますが、明らかにリンクされていません。

template <class T>
class tridiagonal
{
  public:

    // Helper class that let's us tell when the user is
    // assigning into the matrix and when they are just
    // getting values.
    class helper
    {
        tridiagonal<T> &m_parent;

        int m_i, m_j;

    public:
        helper(tridiagonal<T> &parent, int i, int j)
            : m_parent(parent), m_i(i), m_j(j)
        {}

        // Converts the helper class to the underlying
        // matrix value. This doesn't allow assignment.
        operator const T & () const {
            // Just call the const operator() 
            const tridiagonal<T> &constParent = m_parent;

            return constParent(m_i, m_j);
        }

        // Assign a value into the matrix.
        // This is only called for assignment.
        const T & operator= (const T &newVal) {
            // If we are pointing off the diagonal, throw
            if (abs(m_i - m_j) > 1) {
                throw std::exception("Tried to assign to a const matrix element");
            }

            return m_parent.assign(m_i, m_j, newVal);
        }
    };

    tridiagonal();
    ~tridiagonal();

    helper operator()(int i, int j)
    {
        return helper(*this, i,j);
    }

    const T& operator()(int i, int j) const; 

    private:

    T& assign(int i, int j, const T &newVal);

    //holds data of just the diagonals
    T * m_upper;
    T * m_main;
    T * m_lower;
};

int main(int argc, const char * argv[])
{
    tridiagonal<double> mat;

std::cout << mat(0,0) << std::endl;

const tridiagonal<double> & constMat = mat;

std::cout << mat(2,3) << std::endl;

// Compiles and works
mat(2,3) = 10.0;

// Compiles, but throws at runtime
mat(1, 5) = 20.0;

// Doesn't compile
// constMat(3,3) = 12.0;

    return 0;
}

これを行ってからしばらく経ちますので、マトリックスの使用方法によっては、ヘルパークラスにもう少し追加する必要がある場合があります。

実際にこれを実行することは、C++の優れた演習です。:)

于 2012-04-10T01:16:42.147 に答える
0

参照で返すには、指定されたタイプの有効なオブジェクトを返す必要があります。必要なことを実現する最も簡単な方法は、0を表す静的Tオブジェクトを保持し、代わりにそれを返すことです。

または、ポインタを返すこともできます。

于 2012-04-09T23:52:42.387 に答える
0

ダミー値を表すメンバーを追加し、常に0として読み取られるようにします。

template<typename T>
class tridiagonal 
{
    // usual stuff...

    T& operator() (int j, int j)
    {
        // if not explicitly stored, reset to default before returning.
        return stored(i,j)? fetch(i,j) : (m_dummy=T());
    }
private:
    // dummy element used to "reference" elements outside the 3 diagonals.
    T m_dummy;

    // check if (i,j) is on 3 diagonals.
    bool stored (int i, int j) const;

    // access element on 3 diagonals. precondition: stored(i,j)==true.
    T& fetch (int i, int j);

    //holds data of just the diagonals
    T * m_upper;
    T * m_main;
    T * m_lower;
};

技術的に言えば、誰かがあなたをだましてしまう可能性があることに注意してください。

tridiagonal<int> m(4,4);
T * dummy = &m(3,0); // *dummy == 0.
*dummy = 1;          // *dummy == 1.
std::cout << *dummy; // prints 1.

しかし、それは必ずしも問題ではありません。

于 2012-04-09T23:56:39.123 に答える
0

ここでの問題は、不適切なインターフェースです。行列の定義が、行列のすべての要素を個別に設定できるような数値の2D配列である場合、スパースな3次元行列は、逆説的に行列ではありません(正方形が変更可能な長方形ではないのと同じように、 Liskov Substitution Principleに従わない不適切な継承)。

要するに、あなたはあなたが持っているインターフェースで動作するようにそれをハックしようとするよりも、スパースな三重対角行列に合うようにあなたのインターフェースを変更するほうがよいでしょう。そうは言っても、この方法でそれを行わなければならない場合は、おそらく2つのことを行う方がよいでしょう。

  • constアクセサを変更して、T代わりに返すようにconst T&します(ここでは、数値の行列のみを扱っていると想定しています)。0次に、対角線から外れた要素に戻ることができます。
  • constアクセサを変更して、対角線から外れた位置のダミー要素への参照を返し、指を交差させます:)または、throwそのような場合に仕様を変更することもできますが、それは少し不親切かもしれません。

もう1つの代替方法(インターフェイスを適切に作り直す以外)は、Tsの代わりにプロキシオブジェクトを返すことです。ダミー要素のプロキシは、それthrowを使用して値を設定しようとすると、プロキシになります。

于 2012-04-10T01:10:47.217 に答える