Non-const reference of Eigen matrix only bind with dynamic types and not with non-dynamic types [duplicate]
Asked Answered
M

2

0

I have an Eigen (3.4.0) related question that really troubles me. In this C++ code, there's a function pass_by_nonconst_ref which takes a non-const reference to a eigen matrix, and that does some work on the matrix.

#include <iostream>
#include "Eigen/Eigen"

using namespace std;

// A function that takes a non const reference to a vector
void pass_by_nonconst_ref(Eigen::Matrix<float, -1, 1>& vec) {
        // do some work on your matrix...
        vec(0) = 1;
}

int main() {
        // This works without any problem.
        Eigen::Matrix<float, -1, 1> x1(3);
        x1 << 0.0, 0.0, 0.0 ;
        pass_by_nonconst_ref(x1);
        cout << "x = " << x1 << endl ;
        
        // But this does not !!!!
        Eigen::Matrix<float, 3, 1> x2;
        x2 << 0.0, 0.0, 0.0 ;
        // if you uncomment this line, it won't compile...
        // pass_by_nonconst_ref(x2);
        cout << "x = " << x2 << endl ;

        // And to check that x2 is not a temporary?
        Eigen::Matrix<float, 3, 1> x3;
        x3 << 0.0, 0.0, 0.0 ;
        x3(0) = 1;
        cout << "x = " << x3 << endl ;// this works

        return 0;
}

This code only compiles if I pass to the function an object the type Eigen::Matrix<float, -1, 1>. If I use instead Eigen::Matrix<float, 3, 1>, then there is the following compilation error:

error: cannot bind non-const lvalue reference of type ‘Eigen::Matrix<float, -1, 1>&’ to an rvalue of type ‘Eigen::Matrix<float, -1, 1>’
   22 |  pass_by_nonconst_ref(x2);

The only difference (that I see) is that x2 knows it only has 3 rows, whereas x1 has a dynamic number of rows. I am aware that a non-const reference can't bind to a temporary, but I really don't see why x2 can be considered as one and not x1.

I can't understand why I have to specify the dynamic type to make it work. It got me quite curious. Is there something obvious that I am missing ? I suspect it is related to type conversion.

Monaghan answered 25/10, 2022 at 13:15 Comment(1)
Eigen::Matrix<float, -1, 1> and Eigen::Matrix<float, 3, 1> are two different types and your function expects a Eigen::Matrix<float, -1, 1>. You will get the same error if your function parameter was of type int& and you were passing it a double.Amphibolous
B
3

Eigen::Matrix<float, 3, 1> is a different type than Eigen::Matrix<float, -1, 1>. The only reason the function call resolves at all when you call it with x2 is that Eigen supplies an implicit cast operator that knows how to create a dynamic matrix from a fixed-size one. However, what this means is that a temporary is created and passed, which is what is causing your error. See the Eigen documentation at the section "In which cases do functions taking a plain Matrix or Array argument fail?"

That same page provides alternate ways to write your function so that it takes Eigen matrices of all types. If you use this signature, your program compiles and runs:

template <typename Derived>
void pass_by_nonconst_ref(Eigen::MatrixBase<Derived>& vec) {
        // do some work on your matrix...
        vec(0) = 1;
}

Still, beware of temporaries, since Eigen passes around expression types. The documentation page suggests (argh!) in some cases accepting a const reference and casting away the constness. If you pass a plain local Eigen matrix object as I wrote above, you should be fine.

Bayreuth answered 25/10, 2022 at 13:40 Comment(0)
A
2

The problem is that Eigen::Matrix<float, -1, 1> and Eigen::Matrix<float, 3, 1> are two different types and your function's parameter is of type Eigen::Matrix<float, -1, 1>& but the argument you're passing is of type Eigen::Matrix<float, 3, 1>.

You can compare this with if your function's parameter was of type int& and you were passing it a double. In this case, after the conversion from double to int, we will get an rvalue of type int which cannot be bound to an int&.

Similarly in your example, the function accepts a Eigen::Matrix<float, -1, 1> and since you're passing a Eigen::Matrix<float, 3, 1>, that argument can be converted to Eigen::Matrix<float, -1, 1>, but the problem is that that resulting expression is an rvalue which cannot be bound to a Eigen::Matrix<float, -1, 1>& and hence the error.

Amphibolous answered 25/10, 2022 at 13:40 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.