Search code examples
c++eigen

C++ dangling reference to Eigen::Matrix created in function


Would the following example create a dangling reference? (Since the vector<int> is local in the function and the MatrixXi is using a reference to this data). Also considering the Foo constructor takes a reference to the MatrixXi.

#include <Eigen/Dense>
#include <Eigen/Core>
using Eigen::MatrixXi;

static Eigen::MatrixXi CreateMatrix()
{
    std::vector<int> data(4);
    data = { 1, 2, 3, 4 };

    Eigen::MatrixXi mat = Eigen::Map<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(&data[0], 2, 2);
    return mat;
}

class Foo
{
public:
    const MatrixXi matrix;
    Foo(const MatrixXi &matrix) : matrix(matrix) {}
    void Bar() {
        std::cout << matrix << std::endl;
    }
};

int main() 
{
    Foo test(CreateMatrix());
    test.Bar();

    Foo* test2 = new Foo(CreateMatrix());
    test2->Bar();

    std::cin.get();

    delete test2;
}

EDIT: Added the delete test2 to free the memory.

EDIT2:

The actual creation of the MatrixXi in the real problem is based on a parameter of the function and looks like this (C++/CLI):

static Eigen::MatrixXi CreateMatrix(MathNet::Numerics::LinearAlgebra::Matrix<float>^ data)
{
    int length = data->RowCount * data->ColumnCount;
    std::vector<int> data_out(length);

    for (size_t i = 0; i < data->RowCount; i++)
    {
        for (size_t j = 0; j < data->ColumnCount; j++)
        {
            data_out[i * data->ColumnCount + j] = data[i, j];
        }
    }

    Eigen::MatrixXi mat = Eigen::Map<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(&data_out[0], data->RowCount, data->ColumnCount);

    return mat;
}

UPDATE:

To prove the answer from davidhigh is correct I've written the following test:

int main() 
{
    std::vector<int> data = { 1, 2, 3, 4 };
    auto map = Eigen::Map<Eigen::Matrix<int, Eigen::Dynamic, Eigen::Dynamic, Eigen::RowMajor>>(&data[0], 2, 2);
    Eigen::MatrixXi mat = map;
    data[0] = 5;
    std::cout << "mat:\n" << mat << std::endl;
    std::cout << "map:\n" << map << std::endl;
    std::cin.get();
}

This gives console output:

mat:
1 2
3 4
map:
5 2
3 4

So no dangling reference since mat is no longer connected to data (while map is).


Solution

  • Ok, as the status of this thread became a bit confusing, I'll add another answer and detail what I already wrote in the comments.

    • It is ok to return a MatrixXi inside the function CreateMatrix and return it. You perform the creation of the matrix via copying from an Eigen::Map, which is also ok.

    • In your simple example, you could get around the Map by directly initializing the MatrixXi, e.g. via operator<<. In more general scenarios, maybe all you got is a vector, and then your approach is again ok.

    • It's crucial that you return the MatrixXi here. If you would return the Map object, this will -- in general -- lead to a dangling reference. Then, for example, if you call operator() outside, the Map wants to access the vector which is already destructed at exit of CreateMatrix(). (Actually, in your special example, it would work, as you directly use the result to create a Foo, which is again storing the Map inside a matrix -- still, it's not a good idea to write a function that's so error prone).

    • Final point: don't do this raw pointer stuff with test2 which you did in your main function. There's absolutely no use for it here. Use RAII, which is basically your first approach in main.