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::operator[] (size_t i) throw() {
if (i >= rows)
throw InvalidMatrixIndexException();
vector<float> row;
for (int j=0; j < cols; j++)
row.push_back(matrix[i][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::operator[] (...).
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
What's the type of matrix? If it's a std::vector<Vector> you can
simply write
Vector& Matrix::operator[](size_t i)
{
if (i >= rows) throw InvalidMatrixIndexException();
return matrix[i];
}
Also, don't forget const overloads:
Vector const& Matrix::operator[](size_t i) const
{
if (i >= rows) throw InvalidMatrixIndexException();
return matrix[i];
}
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[i][j];
}
template<typename VectorExpression>
MatrixRowVectorRef& operator=(VectorExpression const& ve)
{
if (ve.size() != mat.columns()) throw Something();
// auto && row = mat[i];
for (size_t j=0, e=ve.size(); j<e; ++j) {
mat[i][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[i][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
> 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::operator[] (size_t i) throw() {
> if (i >= rows)
> throw InvalidMatrixIndexException();
>
> vector<float> row;
>
> for (int j=0; j < cols; j++)
> row.push_back(matrix[i][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::operator[] (...).
> 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[i][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.
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
--
Please remove capital 'A's when replying by e-mail
I do not respond to top-posted replies, please don't ask
A little out of order... addressing your actual question first:
> 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::operator[] (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::operator[](size_t i) {
return matrix_data.at(i);
}
--Jonathan
Since everyone else answered your question, I'll point out the FAQ on
the matter:
http://www.parashift.com/c++-faq-lite/operator-overloading.html#faq-13.10
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[i], matrix[i] + cols);
or like this:
row.assign(matrix[i].begin(), matrix[i].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.
>
>yeah, returning a vector, and by value is (on top of being way inefficient) .
Please, this is incorrect !
Returning vector by value is not "way inefficient" in the general case.
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.
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();
}
---------------------------------------------------
In addition to the speed benefits, the above is very simple to write,
very simple to maintain and is far least likely to hide a subtle bugs
than returning a reference to internal data.
The following is probably slower due to the (possibly) unecessary call
to the default constructor and maybe additional indirection.
---------------------------------------------------
void iUseReferenceArg(std::vector<double> & v);
int main()
{
std::vector<double> v;
iUseReferenceArg(v);
}
----------------------------------------------------
It also is more verbose, less obvious, and forces the creation of a
vector that for a period of time has not been initialised with any
relevant data. Although it is a valid vector that has been correctly
constructed, it contains "invalid" data for a time.
This will probably be slower in the common case when a const ref is
not sufficient
---------------------------------------------------
std::vector<double> const & iReturnAConstRefToALocalTemp();
int main()
{
// OK fast enough (but still maybe a bit slower)
std::vector<double> & crv = iReturnAConstRefToALocalTemp();
// slower
std::vector<double> constRefNotSufficient =
iReturnAConstRefToALocalTemp();
}
----------------------------------------------------
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.
--------------------------------------------------------
class Matrix
{
public:
std::vector<double> const & getRow(size_t index) const;
// I'll not even consider a non-const non-const ref version of this...
void modifier(Data data);
private:
std::vector< std::vector<double> > m_matrix;
}
Matrix aMatrix;
// ...
// ...
std::vector<double> const & rv = aMatrix.getRow(12);
// OK
double d = rv.at(4); // OK
// ...
// ...
// inline or even worse, in a difference thread
aMatrix.modifier(data); // OK
// ...
// But now?
double d2 = rv.at(6); // Euh... Is it safe now?
// Or even better
Matrix * pMatrix = new Matrix;
std::vector<double> const & rv = pMatrix->getRow(12);
delete pMatrix;
double d = rv.at(10) // !!!!!!!!!
Note: usually, the above sequence is quite a bit less obvious than
that. It is obfuscated through indirect calling and existence of
multiple classes that return data by reference. It all start with
good intention... the assumption that returing a reference will be
faster... and then we have a database proxy class, that caches the
database data, that for performance returns a reference to its
internal cache but then one adds support for updating or refreshing
the cache somewhere, and then...
Yannick
If you're going to create and return a vector, why not at least do
something like:
Vector Matrix::operator[](size_t i) throw() {
if (i>=rows)
throw InvalidMatrixIndexException();
std::vector<float> row(matrix[i][0], matrix[i][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.
--
Later,
Jerry.
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();
> }
> ---------------------------------------------------
> In addition to the speed benefits, the above is very simple to write,
> very simple to maintain and is far least likely to hide a subtle bugs
> than returning a reference to internal data.
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.
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.
Are you sure that works? Maybe you meant:
std::vector<float> row(&matrix[i][0], &matrix[i][cols]);
Oops -- of course you're right. My apologies for the obviously
incorrect code.
--
Later,
Jerry.
Balog, your exact statement and the quoted text was:
OP:
>> or something like that, and this is not possible returning the Vector
>> object as a value.
Balog:
> yeah, returning a vector, and by value is (on top of being way inefficient)
> leaves you with such code compiling, then surprise the user.
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.
Th OP code was copying data element by elementfrom his Matrix to a
std::vector. This *copying* is ineficient as you now correctly state.
This has nothing to do with returning by value. The cost is in
copying data from whatever format the Matrix class holds it into a
std::vector. Not at all in returing it by value.
You are however correct that you went further in your discussion
afterwards. The rest of your discussion were you are suggesting to
change the internal representation to std::vector< std::vector< ... >
> is a worthwhile consideration (despite breaking implementation hiding), but
one should not make unqualified blanket statement against returning by
value to less experienced programmers.
Yan
P.S.: cut the attitude, that might help.
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.