Один трюк, который вы можете использовать, заключается в том, чтобы метод non-const operator() (int, int) возвращал небольшой вспомогательный объект. Помощник используется для дифференциации между назначением в матрицу и просто вытягиванием значения. Это позволяет вам различать поведение для двух операций.В частности, вы можете бросить, если кто-то пытается присвоить значение, которое должно быть равно нулю.
Этот код, по крайней мере, компилируется для меня в 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++. :)
Я выбрал бросить, когда кто-то пытается изменить недиагональный элемент. Это действительно беспорядочно, но назначение требует, чтобы интерфейс был таким, какой он есть. Спасибо за помощь. –