Re: [eigen] Why do const accessors return by value?

[ Thread Index | Date Index | More lists.tuxfamily.org/eigen Archives ]


On Tue, Mar 31, 2009 at 1:04 PM, Benoit Jacob <jacob.benoit.1@xxxxxxxxx> wrote:
2009/3/31 Patrick Mihelich <patrick.mihelich@xxxxxxxxx>:
> Hi again,
>
> Currently all of the MatrixBase const accessor functions return const
> Scalar, while the non-const version return (of course) Scalar&. Is there a
> rationale for returning by value in the const versions instead of const
> Scalar&?

Yes, in fact we have simply no choice. A MatrixBase may be any
_expression_. As soon as an _expression_ represents a nontrivial
computation (like a sum of two matrices) there is no way that coeff()
could return a reference : a reference to what? the result isn't
stored permanently in memory.

OK, right. But you do not always have to use the lowest-common-denominator behavior (return by value). MatrixBase knows the Derived type and can return by const-reference for Derived types with dense storage (Matrix, Map).

The "standard C++" approach is to give MatrixBase three typedefs: value_type, reference (return type of non-const accessors, convertible to value_type), and const_reference (return type of const accessors, convertible to value_type); then use these to define your accessors. STL/Boost users will recognize these names but you could certainly use friendlier ones like Scalar, ScalarRef, and ScalarConstRef:

ScalarRef operator() (int index);
ScalarConstRef operator() (int index) const;
ScalarRef operator() (int row, int col);
ScalarConstRef operator() (int row, int col) const;
//etc.

For dense storage types:
typedef Scalar& ScalarRef;
typedef const Scalar& ScalarConstRef;

For _expression_ types:
typedef Scalar ScalarRef;
typedef Scalar ScalarConstRef;

> I don't like this inconsistent behavior. Sometimes I want to get a
> pointer to a chunk of memory in a Matrix so I can pass it to some low-level
> function, like foo( &mat(r, c) ).

Above you were talking about MatrixBase; now with Matrix it's
different, indeed the coefficients do exist in memory, and if you look
at the Matrix class, the coeff() methods do return const references,
exactly like you want!

If you have a MatrixBase that happens to be a Matrix, you can always
use the derived() method to get the matrix:

void foo(const MatrixBase& m)
{
   float *ptr = &(m.derived().coeff(i,j));
}

in fact derived() just casts 'this' to a Matrix pointer, and indirects.

OK but this is more verbose, and it's rather surprising that it doesn't do the same thing as m(i,j) or even m.coeff(i,j). I do not think Matrix should override the coeff() methods from MatrixBase with different signatures; more generally I'd suggest that for any MatrixBase method foo, m.foo() and m.derived().foo() should always behave identically.

BTW, there is a mistake in MapBase: const Scalar coeff(int index) const should return const Scalar&.
 

> This breaks when mat is a const type like
> const Matrix&. I can also imagine wanting to use Eigen matrices with types
> that are not trivially cheap to copy (arbitrary precision types, for
> example), when returning by value may result in an unwanted copy.

That's another issue. But the above-mentioned fact that xpressions
have no choice but to return by value, is unavoidable. So I think that
the best solution then, is that the "heavy numeric types" in question
be endowed with a copy-on-write mechanism so that these copies become
shallow copies.

Making the accessor return types smarter I think is much simpler.

Cheers,
Patrick


Mail converted by MHonArc 2.6.19+ http://listengine.tuxfamily.org/