4

テンプレートを使用してマトリックス クラスを作成しました。

template <typename T>
class Matrix
{
    static_assert(std::is_arithmetic<T>::value,"");

public:
    Matrix(size_t n_rows, size_t n_cols);
    Matrix(size_t n_rows, size_t n_cols, const T& value);

    // Functions

    // Operators
Matrix<T>& operator*=(const T& value)

private:
    size_t rows;
    size_t cols;
    std::vector<T> data;
};

行列に数値を掛けるために、次の 2 つの (外部) 演算子を作成しました。

// Inner operator used by the externals ones
template <typename T>
inline Matrix<T>& Matrix<T>::operator*=(const T& value)
{
    for(size_t i(0); i < data.size(); i++)
    {
        data[i] *= value;
    }

    return *this;
}

template <typename T>
inline Matrix<T> operator*(const T& value, const Matrix<T>& matrix)
{
    Matrix<T> tmp(matrix);

    return tmp *= value;
}

template <typename T>
inline Matrix<T> operator*(const Matrix<T>& matrix, const T& value)
{
    return value * matrix;
}

問題は、行列を double として宣言した場合、行列を double でしか乗算できないことです...

Matrix<double> m1(3,3,1.);

5. * m1; // Works
5 * m1; // Doesn't work (5 is an int and not a double)

この動作を修正するにはどうすればよいですか? double に他の算術型を掛けることは可能ですか?

4

3 に答える 3

4

もちろん、テンプレート化された無料の関数とメンバー関数に 2 つのパラメーターを許可するだけです。

例えば:

template <typename T> class Matrix {
  /* ... */
  template <typename U>
  inline Matrix<T>& operator*=(const U& value)
  {
    for(size_t i(0); i < data.size(); i++)
    {
        data[i] *= value;
    }

    return *this;
  }
};

template <typename T, typename U>
inline Matrix<T> operator*(const U& value, const Matrix<T>& matrix)
{
    Matrix<T> tmp(matrix);

    return tmp *= value;
}

これにより、Matrix に意味のないものを掛けようとすると、つまりT*Uが定義されていない場合に、コンパイル エラーが発生します。

于 2012-08-18T12:28:52.047 に答える
1

はい。Matrix クラスの関数を次のように宣言します。

template <typename T> 
class Matrix 
{
public:
    /* ... */
    template <typename S> 
    inline Matrix & operator*=( const S & value );
    /* ... */
};

定義は次のようになります

template <typename T>
template <typename S>
inline Matrix<T>& Matrix<T>::operator*=(const S& value)
{
    for(size_t i(0); i < data.size(); i++)
    {
        data[i] *= value;
    }

    return *this;
}

メンバー関数用。template2回書く必要があります。少し奇妙ですが、これは C++ 構文です。

無料の関数の場合は、書くことができます

template <typename T, typename S>
inline Matrix<T> operator*(const S& value, const Matrix<T> &mat)
{
    Matrix<T> tmp(mat);

    return tmp *= value;
}
于 2012-08-18T12:32:06.693 に答える
0

あなたが見ている問題は、テンプレートの型推定では、推定されたすべての型が完全に一致する必要があるということです。Tあなたの場合、スカラー型と行列型の両方である単一の型引数を取るテンプレートがあります。コンパイラが operation:を認識すると、最初の引数5 * m1は推論しますが、2 番目の引数は推論し、型推論は失敗します。T == intT == double

提案されているように、これには複数のアプローチがあり、2 番目のテンプレート引数を追加できます。

template <typename T, typename S>
Matrix<T> operator*( Matrix<T> m, S scalar ) {
   return m*=scalar;
}

[注: 両方の引数を値で渡します。2 番目の引数は、算術型の場合、値で渡す方が効率的で慣用的であるためです。最初のものは、コピーを関数のインターフェイスに移動することにより、コンパイラがコピーを省略できるようにするためです]。このアプローチは単純ですが、 での実際の乗算は常に で実行されますが、プログラム内のとのoperator*組み合わせごとに1 つ生成されます。SToperator*=T

別のアプローチは、乗算する の型を修正するscalarことです。たとえば、 make it を使用して、乗算される型ごとdoubleに 1 つだけ生成します。operator*T

template <typename T>
Matrix<T> operator*( Matrix<T> m, double scalar ) {
   return m*=scalar;
}

このアプローチには、引数としてoperator*a を取る単一のがあります。double前の例のように、スカラーで 2 つの型変換が必要になる場合があります (たとえば、 を掛けるMatrix<int>5、 に変換5され、 のシグネチャに一致するdoubleように に戻されます。intoperator*=

Matrix3 番目の方法は、同じ型の別の引数を取るテンプレート化されていない関数を作成することです。これは元のコードに最も近く、テンプレートではないというわずかな利点があり、スカラー引数の変換が可能になります。理論的には、そのようなすべての関数を自分で手動で定義できます。

Matrix<int>    operator*( Matrix<int>,    int ) { ... }
Matrix<double> operator*( Matrix<double>, double ) { ... }

しかし、これは非常に簡単にメンテナンスの問題になります。幸いなことに、言語には、テンプレート以外のすべての関数を一般的に定義できる機能があります。構文は最も自然ではないかもしれませんが。free 関数をテンプレートのフレンドとして宣言し、クラス テンプレート定義内で定義するだけです。

template <typename T>
class Matrix {
// ...
   friend Matrix operator*( Matrix m, T scalar ) { return m*=scalar; }
};

クラス template の内部にいるので、 (引数なしで) を使用して現在のインスタンス化 ( 、 ...) を参照Matrixできます[これは明らかに重要ではないように思えるかもしれませんが、テンプレートをいつ参照するかを理解することが重要です。テンプレートから生成されたクラスを参照する場合]。関数の 2 番目の引数は です。繰り返しますが、これはクラス テンプレートのジェネリックではなく、現在インスタンス化されている型 ( 、...) です。MatrixMatrix<int>Matrix<doubleMatrixTTintdouble

この言語friendでは、宣言を持つクラス内で関数を定義できます。これにより、名前空間レベルで関数が定義されますが、宣言は Argument Dependent Lookup を介してのみ検出されます

テンプレートの特定のインスタンス (たとえばMatrix<int>) をインスタンス化してオペレーターを呼び出すたびに、コンパイラーは無料の関数を生成します。関数はテンプレート化されていないため、引数の変換が可能であり、したがって forに変換しMatrix<int> mて呼び出すことができます。m * 5.5.int

于 2012-08-18T21:13:00.623 に答える