Is there a way to bypass "returning reference to local variable" problem?

Viewed 249

I have a homework exercise. I'm almost sure it's unsolvable the way they ask it. However, I'm interested if you guys have any solution for the problem mentioned below because it seems like something that often occurs.

The description is not long, so I share it with you below:

A matrix S ∈ R n×n is skewsymmetric if it holds that S(Transpose) = −S. Derive from the class SquareMatrix from the lecture the class SkewSymmetricMatrix. Use a vector of length n(n − 1)/2 to store the matrix entries. Implement constructors, type casting and a suitable access to the coefficients.

The problem occurs while trying to provide the access, because the virtual access method defined in SquareMatrix returns a reference.

const double& SquareMatrix::operator()( int j, int k ) const
{
    assert( j >= 0 && j < m );
    assert( k >= 0 && k < n );
    return coeff[ j + k * m ];
}

However, I can't return reference to the not stored variables. The following code is just for demonstrating my problem. In this case the j > k block would obviously not work.

const double& SkewSymmetricMatrix::operator()( int j, int k ) const
{
    assert( j >= 0 && j < size() );
    assert( k >= 0 && k < size() );
    if( j < k )
    {
        const double* coeff = getCoeff();
        return coeff[ j * ( j - 1 ) / 2 + k ];
    }
    else if ( j > k )
    {
        const double* coeff = getCoeff();
        return -coeff[ k * ( k - 1 ) / 2 + j ];
    }
    else
    {
        return const_zero;
    }
}

Do you have any suggestions that how to provide a proper access operator while trying to reduce the use of memory by

  1. storing less elements
  2. and calculating the non-stored desired elements from the actually stored ones?
3 Answers

One idea in comments was using a temporary private member and return a reference to it. That would be a really bad idea:

If you use a reference to a private member, the value changes at next call. If the caller uses:

const double& val1 = my_square_matrix(2, 1);
const double& val2 = my_square_matrix(3, 1);
double sum = val1 + val2;

The result will be val2 + val2, not val1 + val2, because the wrong value is referenced.

But there are two solutions to fulfill the requirements:

  1. Implement a get-method and throw an exception, if the parentheses-operator is called.

  2. Make a second member vector, but leave it empty. Always if you access a not existent element, check the vector (and resize it once to n(n-1)/2 if empty, but never to another size!), and write the value at the desired position. Then, return a reference to this position. Only once resize the vector, because it can allocate new memory if resized, so old references would get invalid.

The problem requires using a vector of n*(n-1)/2 elements, but it doesn't specify the type of element that that vector should contain.

A matrix S ∈ R n×n is skewsymmetric if it holds that S(Transpose) = −S. Derive from the class SquareMatrix from the lecture the class SkewSymmetricMatrix. Use a vector of length n(n − 1)/2 to store the matrix entries. Implement constructors, type casting and a suitable access to the coefficients.

Given that we have no capacity to change the interface given by the SquareMatrix class, we will have to resort to solving this problem with a solution that is technically compliant, even if it doesn't follow the spirit of the professor's request (which is impossible).

Each element of the vector will contain two double values, one corresponding to the element m_ij, and the other corresponding to m_ji (for i < j). We can call these the left and right elements:

struct mirror {
    double left; 
    double right; 

    void assignLeft(double val) {
        left = val; 
        right = -val;
    }
    void assignRight(double val) {
        left = -val;
        right = val;
    }
}; 

We may use a vector of n*(n-1) / 2 elements of type mirror, and we return either the left or right members based on which index is asked for:

const double& SkewSymmetricMatrix::operator()( int j, int k ) const
{
    assert( j >= 0 && j < size() );
    assert( k >= 0 && k < size() );
    if( j < k )
    {
        const mirror* coeff = getCoeff();
        return coeff[ j * ( j - 1 ) / 2 + k ].left;
    }
    else if ( j > k )
    {
        const mirror* coeff = getCoeff();
        return coeff[ k * ( k - 1 ) / 2 + j ].right;
    }
    else
    {
        return const_zero;
    }
}

Such is life in Moscow.

If you want const double& SquareMatrix::operator()(int j, int k) const { ... } to override a virtual method, thus keep returning a const double&, then I don't know how to do it.

If you are allowed to change the signature, then create

class ValueAccessor {
  double &value;
  bool is_negated;
 public:
  ValueAccessor& operator=(double other) {
    value = is_negated ? -other : other;
    return *this;
  }
  operator double() const {
    return is_negated ? -value : value;
  }
  ...
};

, and return a ValueAccessor from operator()(...).

Related