Search code examples
c++pass-by-referenceeigenauto

Erroneous use of auto type specifier with Eigen objects


I have a main function which looks like:

#include <iostream>
#include <vector>
#include <Eigen/Dense>

using Eigen::Vector3d;
using namespace std;

void ext_func(vector<Vector3d> &a, vector<Vector3d> &b, vector<Vector3d> &c);
int main()
{
    vector<Vector3d> a = {};
    a.push_back(Vector3d(1.,1.,0.));
    a.push_back(Vector3d(2.,1.,0.));

    vector<Vector3d> b = {};
    b.push_back(Vector3d(.5,.7,2.));
    b.push_back(Vector3d(0.,.2,-1.2));

    vector<Vector3d> c = {};
    c.push_back(Vector3d::Zero());
    c.push_back(Vector3d::Zero());

    ext_func(a,b,c);

}

From main, I call an external function:

void ext_func(vector<Vector3d> &a, vector<Vector3d> &b, vector<Vector3d> &c)
{
    double fac = 0.01;
    for (size_t i = 0; i < a.size(); i++) {
        auto a_temp = a[i] + fac * b[i] / 50.;
        cout << "Before" << endl << fac*a_temp << endl;
        a[i] = a_temp;
        c[i] += fac*a_temp;
        cout << "After" << endl << fac*a_temp << endl << endl;
    }
}

The output is:

Before
 0.010001
0.0100014
    4e-06
After
 0.010002
0.0100028
    8e-06

Before
     0.02
0.0100004
 -2.4e-06
After
     0.02
0.0100008
 -4.8e-06

Which is not what I want to get (I want a_temp to stay constant once it's assigned). I understand that the problem is that I assign a new value to &a and so when I'm calling a_temp I'm basically incrementing a second time my initial a[i]. The problem is solved by replacing auto with Vector3d or Vector3d&&.

What I'm not sure about is what is auto really doing here.


Solution

  • When calculating with matrices in the eigen library, the overloaded operators return an object representing the calculation expression. This expression is not yet evaluated (ie. the actual calculation is not yet performed).

    In your example, the type derived by auto is Eigen::CwiseBinaryOp<Eigen::internal::scalar_sum_op<double>, const Eigen::Matrix<double, 3, 1>, const Eigen::CwiseUnaryOp<Eigen::internal::scalar_quotient1_op<double>, const Eigen::CwiseUnaryOp<Eigen::internal::scalar_multiple_op<double>, const Eigen::Matrix<double, 3, 1>>>>.

    The actual evaluation of the expression is done when the result is needed (eg. when assigning to a variable, printing, etc.).

    Since a_temp in your case is the expression (not the result), the expression is evaluated multiple times :

    • when printing it as part of the Before line
    • when assigning to a[i]
    • when assigning to c[i]
    • when printing it as part of the After line

    The second of these ends up modifying one of the operands of the expression, so subsequent evaluations of the expression will have a different result.