Matrix Assignment
Assignment of matrices verifies the dimensions, i.e.
A= B;
is only correct when A and B have the same number of rows and columns. If you need for some (probably avoidable) reason need to assign matrices of different dimension you must explicitly change it:
We strongly recommand to avoid this because you risk to hide errors in your program. Assigning matrices of different dimension is in most cases an indication for an error. If memory consumption is the reason for such an assignment you should try to destroy unused matrices (e.g. by introducing additional blocks and define matrices within) and define new ones.
There is one exception that allows for the change of dimension, when the target has dimension 0 by 0. These matrices are considered as stem cells, they can become whatever desired but once they get a non-trivial dimensionality they obey algebraic compatibility rules. Default constructors of matrix types always create 0 by 0 matrices. This simplifies the implementation of generic setter function:
dense2D<double> A; some_setter(A);
For numeric reliability we refrain from shallow copy semantics, cf. Why Not Using Shallow Copy in Numerical Software. There is an important exception that covers most algorithmically interesting cases where shallow copies are legitimate. Resulting objects of functions and operators exist only once and are destroyed after assignments. In the C++ community such arguments that can only appear on the right-hand side of an assignment are called rvalue. Rvalues that own their data can be copied shallowly without affecting the semantics. David Abrahams et al. formalized this approach and implemented the move library in the Adobe Source Libraries (ASL).
MTL4 uses move semantics to assign matrices of the same type when the source is an rvalue. Therefore, returning matrices (or vectors) in functions is rather cheap if the target has the same type, e.g.:
// File: move_matrix.cpp #include <iostream> #include <boost/numeric/mtl/mtl.hpp> using namespace mtl; // Return a matrix with move semantics // (argument is only for type detection) template <typename Matrix> Matrix f(const Matrix&) { Matrix A(3, 3); A= 5.0; return A; } int main(int, char**) { dense2D<double> A(3, 3), B(3, 3); dense2D<float> C(3, 3); B= f(A); // Result of f is copied shallowly, move semantic kicks in B= A; // Deep copy because it's not an rvalue C= f(A); // Deep copy because C has different type return 0; }
Assigning expressions to matrices or vectors does not use move semantics because MTL4 operators are implemented with expression templates and avoid unnecessary copies with other techniques. We assume that carefully designed algorithms use assignments of variables to copy their contents and that after changing one of the two variables the other still have the same value.
x= y;
y= z; // x has still the same value as before this operation
Resuming this, you can (and should) take an algorithm from a text book, implement it with the same operators and functions using MTL4
Please not that move semantics relies on compiler-intern optimizations that some compilers do not perform without optimization flags, e.g. MSVC. Therefore, the tests for move semantics are not in the regular test directory but in another one where the compilation uses optimization flags. On MSVC we noticed that for higher optimization some locations were equal that should not. This could be worked around by inserting print-outs of pointers. (Nevertheless this is not satisfying and help would be welcome.)
Last but not least, we want to thank David Abrahams and Sean Parent who helped to understand the subtle interplay between details of the implementation and the behavior of the compiler.
Return to Vector Assignment Table of Content Proceed to Vector Expressions
Matrix Assignment -- MTL 4 -- Peter Gottschling and Andrew Lumsdaine
-- Gen. with
rev. 7542
on Sat Aug 11 2012 by doxygen 1.7.6.1 -- © 2010 by SimuNova UG.