C++ 对在函数中创建的 Eigen::Matrix 的悬空引用

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

下面的例子会创建悬空引用吗? (因为 vector<int> 在函数中是局部的,而 MatrixXi 正在使用对此数据的引用)。还考虑到 Foo 构造函数引用了 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;
}

编辑: 添加了 delete test2 以释放内存。

编辑2:

实际问题中 MatrixXi 的实际创建是基于函数的参数,看起来像这样 (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;
}

更新:

为了证明 davidhigh 的回答是正确的,我编写了以下测试:

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();
}

控制台输出如下:

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

所以没有悬空引用,因为 mat 不再连接到数据(而 map 是)。

您从 CreateMatrix() 调用返回的垫子保存着临时 std::vector 的地址,该地址超出了该函数的范围。 无法保证 address 仍会保留您想要的内容。 您需要使 std::vector 更永久。它必须存在于 CreateMatrix() 之外,否则您将遇到麻烦。

好的,由于这个线程的状态变得有点混乱,我将添加另一个答案并详细说明我已经在评论中写的内容。

  • return一个MatrixXi函数里面CreateMatrix和return就可以了。您通过从 Eigen::Map 复制来创建矩阵,这也可以。

  • 在您的简单示例中,您可以通过直接初始化 MatrixXi 来绕过 Map,例如通过 operator<<。在更一般的情况下,也许你得到的只是一个向量,那么你的方法又可以了。

  • 你 return 这里的 MatrixXi 很重要。如果您将 return Map 对象,这通常会导致悬空引用。然后,例如,如果您在外部调用 operator(),则 Map 想要访问在 CreateMatrix() 退出时已经被破坏的向量。 (实际上,在您的特殊示例中,它会起作用,因为您直接使用结果创建 Foo,它再次将 Map 存储在矩阵中——仍然,这不是一个好主意编写一个非常容易出错的函数)。

  • 最后一点:不要用 test2 做你在主函数中做的原始指针。在这里完全没有用。使用 RAII,这基本上是您在 main 中的第一种方法。