How to return a reference, when I really need it


B

BlackLight

Hi,

I'm actually developing a tiny C++ library for managing linear algebra
operations, but I have a problem in managing the reference to an
object as return value. This is the method I wrote:

Vector Matrix::eek:perator[] (size_t i) throw() {
if (i >= rows)
throw InvalidMatrixIndexException();

vector<float> row;

for (int j=0; j < cols; j++)
row.push_back(matrix[j]);

return Vector(row);
}

where Matrix is a class I wrote for matrices management, and Vector is
another class (fundamentally a wrapping around vector<float> I wrote
for doing cool mathematical tricks, like sums, scalar products, norms
etc.). This is the actual implementation, but I would like to return a
reference to the Vector object, i.e. Vector& Matrix::eek:perator[] (...).
I need it to do tricks like

Matrix A;
A[0][0] = 1.0;

or something like that, and this is not possible returning the Vector
object as a value. But if I declare a local Vector object and I return
its reference, I've got a problem as returning the reference of a
local variable. Has any of you a solution for this?

Thanks,
BlackLight
 
Ad

Advertisements

S

SG

Vector Matrix::eek:perator[] (size_t i) throw()  {
    if (i >= rows)
        throw InvalidMatrixIndexException();
    vector<float> row;
    for (int j=0; j < cols; j++)
        row.push_back(matrix[j]);
    return Vector(row);
}


What's the type of matrix? If it's a std::vector<Vector> you can
simply write

Vector& Matrix::eek:perator[](size_t i)
{
if (i >= rows) throw InvalidMatrixIndexException();
return matrix;
}

Also, don't forget const overloads:

Vector const& Matrix::eek:perator[](size_t i) const
{
if (i >= rows) throw InvalidMatrixIndexException();
return matrix;
}

If it is something different you can return a proxy -- your own "row
vector reference" so to speak.

class MatrixRowVectorRef
{
private:
Matrix & mat; // a reference member disables the...
size_t i; // compiler-generated assignment operator
public:
MatrixRowVectorRef(Matrix & m, size_t i)
: mat(m), i(i) {}

size_t size() const {
return mat.columns();
}

float& operator[](size_t j) const {
return mat[j];
}

template<typename VectorExpression>
MatrixRowVectorRef& operator=(VectorExpression const& ve)
{
if (ve.size() != mat.columns()) throw Something();
// auto && row = mat;
for (size_t j=0, e=ve.size(); j<e; ++j) {
mat[j] = ve[j];
// alternativly: row[j] = ve[j];
}
}
}

and another version for a reference to const:

class MatrixRowVectorConstRef
{
private:
Matrix const & mat;
...

You may also want to use the function call operator for accessing a
matrix' element:

float& operator()(size_t i, size_t j) {
return matrix[j];
}

You may also want to checkout the "Boost uBLAS" library. It takes some
getting used to, though. It's heavy on template trickery and you have
to understand the "concept of concepts". The exact types are less
important than the concepts the types model.

Cheers!
SG
 
V

Victor Bazarov

BlackLight said:
I'm actually developing a tiny C++ library for managing linear algebra
operations, but I have a problem in managing the reference to an
object as return value. This is the method I wrote:

Vector Matrix::eek:perator[] (size_t i) throw() {
if (i >= rows)
throw InvalidMatrixIndexException();

vector<float> row;

for (int j=0; j < cols; j++)
row.push_back(matrix[j]);

return Vector(row);
}

where Matrix is a class I wrote for matrices management, and Vector is
another class (fundamentally a wrapping around vector<float> I wrote
for doing cool mathematical tricks, like sums, scalar products, norms
etc.). This is the actual implementation, but I would like to return a
reference to the Vector object, i.e. Vector& Matrix::eek:perator[] (...).


WHY??? 8-O
I need it to do tricks like

Matrix A;
A[0][0] = 1.0;

or something like that, and this is not possible returning the Vector
object as a value. But if I declare a local Vector object and I return
its reference, I've got a problem as returning the reference of a
local variable. Has any of you a solution for this?

Yes, it's called a proxy object. *Instead* of returning a Vector, you
return a RowProxy (a nested class of Matrix), which itself does not
contain data (like your Vector that wraps vector<float>) but keeps the
reference to the matrix with which it's associated and performs the
operations that you need (like indexing). Here is a sample
implementation (not tested):

class Matrix
{
...

class RowProxy
{
Matrix& rmatrix;
size_t row;
friend class Matrix; // so it can create the proxy

// note that the constructor is private
RowProxy(Matrix& rm, size_t r) : rmatrix(rm), row(r) {}

public:
float& operator[](size_t j) {
rmatrix.matrix[row][j];
}

// conversion to 'Vector' (only if you need one)
operator Vector() const {
// this is where you copy your stuff
Vector v;
for (...
return v;
}
};

friend class RowProxy; // so it can access 'matrix' member

RowProxy operator[](size_t row) {
return RowProxy(*this, row);
}
};

Essentially, it's "lazy evaluation" of sorts.

V
 
J

Jonathan Lee

or something like that, and this is not possible returning the Vector
object as a value. But if I declare a local Vector object and I return
its reference, I've got a problem as returning the reference of a
local variable. Has any of you a solution for this?

1) Use RVO. See the Parashift C++ faq, item 10.9.
2) Binding the automatic to a const reference will extend its lifetime
ex.,

const Vector& t = somematrix[3];

But it _has_ to be a const reference so you're limited with what
you can do with it.

Some other comments:
Vector Matrix::eek:perator[] (size_t i) throw()  {
        if (i >= rows)
                throw InvalidMatrixIndexException();

Why do you have throw() when you actually throw? (rhetorical, don't
really need to answer..)
(fundamentally a wrapping around vector<float> I wrote for doing
cool mathematical tricks, like sums, scalar products, norms
etc.).

I'd inherit from std::valarray instead and you'll get some of this
stuff for free.
Matrix A;
A[0][0] = 1.0;

You could save your data as a std::vector of std::vector (or
valarray, as I mentioned) rows. Then just return the reference
to the row. You'd get this (particular) notation for free.

Ex.,

std::vector< std::vector<float> > matrix_data;
typedef std::vector< std::vector<float> >::reference ref

ref Matrix::eek:perator[](size_t i) {
return matrix_data.at(i);
}

--Jonathan
 
B

Balog Pal

BlackLight said:
where Matrix is a class I wrote for matrices management, and Vector is
another class (fundamentally a wrapping around vector<float>

float will very likely burn you or the users... Use double unless absolutely
sure precision and roundings are accounted for everywhere.

Also, are you sure you want to write yet-another-homegrown matrix class
instead of using an existing, ready with all your functions and passed years
of testing and reviews?

And do some from scratch and top of your head, instead at least reading a
couple of such implementations? On the way learning the answers to this
question and many others you will encounter shortly.
Vector Matrix::eek:perator[] (size_t i) throw() {
if (i >= rows)
throw InvalidMatrixIndexException();

vector<float> row;

for (int j=0; j < cols; j++)
row.push_back(matrix[j]);

return Vector(row);
}

I wrote
for doing cool mathematical tricks, like sums, scalar products, norms
etc.). This is the actual implementation, but I would like to return a
reference to the Vector object, i.e. Vector& Matrix::eek:perator[] (...).
I need it to do tricks like

Matrix A;
A[0][0] = 1.0;

or something like that, and this is not possible returning the Vector
object as a value.

yeah, returning a vector, and by value is (on top of being way inefficient)
leaves you with such code compiling, then surprise the user.
But if I declare a local Vector object and I return
its reference, I've got a problem as returning the reference of a
local variable.

Indeed, to return a vector by ref you need a stable vector, i.e. switch
internal representation to vector< vector< T > >. That would certainly mean
oblugation to resize all the row-vectors, separate mem-allocation, losing
locality and so on.

Alternatively you may return a pointer instead of vector: & matrix[0],
provided the memory layout is compatible -- then op [] works as expected,
though you lose features of vector, and gain some danger of pointers.

Or you can return a proxy-vector class by value that has op[] and captures
enough info to find way to its source. ...

IIRC Matthew Wilson's Imperfect C++ has a comprehensive section on creating
wrappers for multi-dim arrays and the zillion of problems it brings.
And the topic must have good coverage in general.
 
Ad

Advertisements

J

Juha Nieminen

BlackLight said:
Vector Matrix::eek:perator[] (size_t i) throw() {
if (i >= rows)
throw InvalidMatrixIndexException();

vector<float> row;

for (int j=0; j < cols; j++)
row.push_back(matrix[j]);

return Vector(row);
}


Not an answer to your question, but I would like to note that your
function there is really, really heavy.

First of all, allocating a vector every time that function is called
is going to be a rather heavy operation, especially for the intended
purpose.

Secondly, even if you simply *must* do that and there's no way around
it, at the very least *reserve* the necessary capacity in the vector
before filling it. That way you will completely avoid reallocations,
which is an even heavier operation, and can potentially happen several
times during that loop. (It will also lessen memory fragmentation.)

Thirdly, learn to use the handy functions each STL container offers. I
don't know how your 'matrix' member is implemented, but you will
probably be able to do it like this:

row.assign(matrix, matrix + cols);

or like this:

row.assign(matrix.begin(), matrix.end());

depending on what kind of data container that 'matrix' is.

Not that this will be relevant in your final version of that function,
but just wanted to comment.
 
J

Jerry Coffin

Hi,

I'm actually developing a tiny C++ library for managing linear algebra
operations, but I have a problem in managing the reference to an
object as return value. This is the method I wrote:

Vector Matrix::eek:perator[] (size_t i) throw() {
if (i >= rows)
throw InvalidMatrixIndexException();

vector<float> row;

for (int j=0; j < cols; j++)
row.push_back(matrix[j]);

return Vector(row);
}


If you're going to create and return a vector, why not at least do
something like:

Vector Matrix::eek:perator[](size_t i) throw() {
if (i>=rows)
throw InvalidMatrixIndexException();

std::vector<float> row(matrix[0], matrix[cols]);
return Vector(row);
}

OTOH, you might also want to look at std::valarray and its
associates. You seem to be basically reinventing a lot of that.
 
B

Balog Pal

Yannick Tremblay said:
Please, this is incorrect !

Returning vector by value is not "way inefficient" in the general case.

This forum starts acting weird, within few days I observe several claims
that positive numbers are not necessarily greater than zero. :-o

returrning a vector filled with a copy of a row IS inefficient compared to
retrirning a reference to that row. In ANY case. Sorry if that breaks
anyones world, but that is simple fact that optimizing away *more* copies
over the first will not eliminate. 1 copy on dynamic store is more than 0
copies.
It may even be faster and more efficient than returning by reference
in the majority of usage scenario because of the capability of the
compiler to optimise away any superflous copy and allocation via RVO.

You're probably creating some strowman example for comparision instead of
looking the original context of the statement.



So the following is probably the fastest in a large set of very common
usage scenario.
---------------------------------------------------
std::vector<double> iReturnALargeVector();

int main()
{
std::vector<double> v = iReturnALargeVector();
}

Your additional benefits are made up, and the rest belongs to a different
discussion that is not denied, but was not in the scope.

And OP's original problem was that he wants to MODIFY the content of the
original container, not poking a copy of it, so this would not apply as
solution even if everything else was true.

Returning a reference to internal data to a class (possibly using a
proxy) may in some circumstances have speed benefits. But is has very
significant drawbacks because of the risk it introduce in the code.

that is well described in Meyers, Sutter and other morality guides. Still if
you want an interface where the stuff behaves like lvalue, you don;t have
too many options. The traditional Set...( value ) like interface avoids
certain traps, but may force client code to a suboptimal look. Even if
you have all the luck with the optimization.

Designing a good interface measuring all the attributes against each other
never was a trivial task. I suggested OP to read and analyze existing
libraries for a good reason.
 
J

Juha Nieminen

Balog said:
returrning a vector filled with a copy of a row IS inefficient compared to
retrirning a reference to that row.

I believe he was talking about *creating* a vector and returning it by
value, vs. *creating* a vector (somewhere) and returning a reference to it.

With return value optimization the vector instance will be created (in
optimal conditions) on the caller's stack, which will usually be more
efficient than creating it somewhere else and returning a reference to it.

But of course avoiding the *creation* of the vector in the first place
is the most optimal solution.
 
J

Juha Nieminen

Jerry said:
std::vector<float> row(matrix[0], matrix[cols]);


Are you sure that works? Maybe you meant:

std::vector<float> row(&matrix[0], &matrix[cols]);
 
Ad

Advertisements

Ad

Advertisements

B

Balog Pal

Yannick Tremblay said:
Balog, your exact statement and the quoted text was:

OP:

Nowhere in that do you state that copying data from the in memory
Matrix object into a std::vector is what is inneficient, you simply
state that returning a vector by value is inneficient.

Guess whoever read the whole post in its context can figure out, while whose
intention is to lawyer in a problem will surely able, and then look the
wiser in his own eyes...

Hopefully the OP got enough input to learn, I'm not interested in bickering.
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top