This forum has been archived. All content is frozen. Please use KDE Discuss instead.

Silent copy when accidentally getting wrong reference

Tags: None
(comma "," separated)
User avatar
alecjacobson
Registered Member
Posts
26
Karma
0
I'm a little befuddled why 1) this compiles or 2) this runs so slowly:


Code: Select all
#include <iostream>
#include <Eigen/Core>

Eigen::Matrix<double,Eigen::Dynamic,3> A(100000000,3);
void wtf()
{
  const Eigen::MatrixXd & silent_cpy = A;
  std::cout<<"^~~~~~~ why does that take so long."<<std::endl;
}
void yay()
{
  const Eigen::Matrix<double,Eigen::Dynamic,3> & no_cpy = A;
  std::cout<<"^~~~~~~ yay!?@?#$%..."<<std::endl;
}
int main(int argc, char* argv[])
{
  std::cout<<"Your about to wait a while..."<<std::endl;
  wtf();
  std::cout<<"But this is fast..."<<std::endl;
  yay();
}


If you replace

Code: Select all
  const Eigen::MatrixXd & silent_cpy = A;


with
Code: Select all
Eigen::MatrixXd & silent_cpy = A;

then it no longer compiles.

I'm guessing that what's happening is that the compiler sees the type mismatch above but also sees that the reference will be const and that there's a copy constructor that will convert Eigen::Matrix<double,Eigen::Dynamic,3> to Eigen::MatrixXd.

It's a bit disappointing because this discourages good programming habits like creating short names to const references. Of course one can say, "Just always use the right type" but there's no warning here and it's an easy trap to fall into with some many template parameters.
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
You found the correct answer by yourself. Forbidding such conversions would be unexpected for a lot of other users as this would lead to situations like:
Code: Select all
foo(const MatrixXd &a);
Matrix3d a, b;
MatrixXd A = a; // forbidden
MatrixXd B = a+A; // allowed
MatrixXd C = a*1; // allowed
foo(A+B); // allowed
foo(a+b); // allowed
foo(a); // forbidden?
foo(a*1); // allowed
[CODE]
, and only raising a warning would not be possible in c++. If c++11 is ok for you, then auto might be the right approach, but be aware that, e.g.:
[code]
auto X = A * B;
X.minCoeff();
X.maxCoeff();
[/code]
will evaluate A*B twice as X will only be an abstract representation of A*B and not a Matrix<...>.

Our class Ref<> might also be a solution to you, as:
[code]
Matrix<double,Eigen::Dynamic,3> A(100000000,3);
Ref<const MatrixXd> no_cpy(A);

won't trigger a copy, but you will loose the compile-time knowledge that no_cpy has only 3 columns.
User avatar
alecjacobson
Registered Member
Posts
26
Karma
0
Thanks for the quick response. So, do I understand correctly that the problem is, in order to allow copy conversions of plain objects (non references, non pointers) in C++ one _must_ also allow this "copy during assignment to const reference" behavior?

The thing that confuses me a little is that it _only_ happens if the reference is const. Is there no way to override the copy to "const ref" operator?
User avatar
ggael
Moderator
Posts
3447
Karma
19
OS
You are right. And we cannot distinguish between a copy conversions of plain objects versus a copy to a const reference because what happens when doing:

Matrix3f a;
const MatrixXd &A = a;

is that the C++ compiler create a temporary MatrixXd object from a:

MatrixXd tmp(a);

and then this temporary is bound to the const reference:

const MatrixXd &A = tmp;

This does not work with non const references because in C++, only const references can be bound to temporaries.


Bookmarks



Who is online

Registered users: Bing [Bot], Google [Bot], Yahoo [Bot]