c++ eigen 3.5,特征图不使用命名 return 值优化?

c++ eigen 3.5, eigen maps don't use named return value optimisation?

我对 C++ 及其特征库还很陌生。 当从函数 returning 特征映射(指向 std::vector)或 returning std::vector 时,我遇到了我认为有点奇怪的行为从一个函数,然后应用一个特征映射到它。我认为这与命名 return 值优化 (NRVO) 有关。

我试过三种方法来做到这一点。前两个给出了不想要的结果,而第三个方法给出了我想要的结果(见下面的输出)。 这是说明这三种方法的最小工作示例:

int main(){
    double i1 = 1, i2 = 2, i3 = 3, i4 = 4, i5 = 5, i6 = 6, i7 = 7, i8 = 8; 

    //method 1
    Eigen::Map< Eigen::MatrixXd > x_m1 = get_map_test(i1, i2, i3, i4);
    Eigen::Map< Eigen::MatrixXd > y_m1 = get_map_test(i5, i6, i7, i8); 

    //method 2
    Eigen::Map< Eigen::MatrixXd > x_m2(get_vec_test(i1, i2, i3, i4).data(), 2, 2);
    Eigen::Map< Eigen::MatrixXd > y_m2(get_vec_test(i5,i6,i7,i8).data(), 2, 2);

    //method 3
    std::vector<double> x_v3 = get_vec_test(i1, i2, i3, i4);
    std::vector<double> y_v3 = get_vec_test(i5, i6, i7, i8);
    Eigen::Map< Eigen::MatrixXd > x_m3(x_v3.data(), 2, 2);
    Eigen::Map< Eigen::MatrixXd > y_m3(y_v3.data(), 2, 2);
}  //end main

//used in method 1
Eigen::Map< Eigen::MatrixXd > get_map_test(double a, double b, double c, double d) {
    std::vector<double> t_v = {a,b,c,d};
    return Eigen::Map< Eigen::MatrixXd >(t_v.data(), 2, 2);
 }

 //used in methods 2 and 3
std::vector<double> get_vec_test(double a, double b, double c, double d) {
    std::vector<double> t_v = {a,b,c,d};
    return t_v;
 }

方法一:

我开始只是将值传递给一个函数,然后在函数中,将这些值存储在 std::vector 中,然后 returning 一个指向这个的特征映射(特征矩阵的)向量。

然后我得到了矩阵 x_m1 和 y_m1 的值以及它们的 .data() 值的以下输出:

x_m1:
4.94066e-324            7
4.94066e-324            8
y_m1:
4.94066e-324            7
4.94066e-324            8
x_m1.data():
0xaea150
y_m1.data():
0xaea150

地图的输出显然不是我们想要的。此外,地图不应该指向同一个向量。

方法二:

接下来我尝试 returning std::vector,并使用它在与函数 return.

相同的行中初始化地图

对应的结果为:

x_m2:
4.94066e-324            7
4.94066e-324            8
y_m2:
4.94066e-324            7
4.94066e-324            8
x_m2.data():
0xaea150
y_m2.data():
0xaea150

可以看出,这导致相同的结果,但我可以理解为什么 NRVO 不会在这里发生。

方法三:

最后我尝试 returning 向量,然后在以下行初始化地图:

x_m3:
1 3
2 4
y_m3:
5 7
6 8
x_m3.data():
0xaea150
y_m3.data():
0xaea180

这给出了我想要的结果,我猜 std::vector 上的 NRVO 就是原因。

尝试理解结果:

如前所述,我认为该方法是否给出预期结果的决定因素是 NRVO 是否发生。

对于第三种方法,我知道 NRVO 发生在 std::vector。

我可以看到第二种方法,当函数退出时,向量中的数据可能会被破坏,所以在调用函数中留下了 'dangling vector'(因此我假设 NRVO 为 std::vector 仅当您明确设置函数 return 等于与 return?? 相同类型的向量时才会发生。 并且由于数据被破坏,相同的地址可用于第二次函数调用以存储双精度数(遭受相同的命运)。

所以我想我的主要问题(除了澄清我刚才所做的陈述之外)是,NRVO 不适用于本征图吗?这有什么特别的原因吗?

我正在使用 eigen 3.5,c++11 和 g++

提前致谢

简要查看 Eigen::Map 的文档,我的理解是它是一个非拥有的包装器,围绕着您提供的指针所指向的数据。这使您可以对此数据执行操作,而无需制作额外的数据副本。

正在查看"method 1":

Eigen::Map< Eigen::MatrixXd > get_map_test(double a, double b, double c, double d) {
    std::vector<double> t_v = {a,b,c,d};
    return Eigen::Map< Eigen::MatrixXd >(t_v.data(), 2, 2);
}

你return一个Eigen::Map,其中指针指的是向量t_v分配的内存。当 t_v 在函数末尾超出范围时,内存被释放,但 Eigen::Map 仍在映射现在悬挂指针指向的内存。不好。

方法 2 如下所示:

std::vector<double> get_vec_test(double a, double b, double c, double d) {
    std::vector<double> t_v = {a,b,c,d};
    return t_v;
}

这个函数本身不会发生任何不好的事情。您只是在制作 4 个双打的矢量,并按值 return 它。但是,您随后使用它执行此操作:

Eigen::Map< Eigen::MatrixXd > x_m2(get_vec_test(i1, i2, i3, i4).data(), 2, 2);

get_vec_test 编辑的 std::vector<double> 对象 return 是一个临时对象。您提供指向该临时文件中数据的指针 Eigen::Map。在评估完整表达式 x_m2 的最后,也留下了一个悬空指针,因为临时 std::vector<double> return 的生命周期从 get_vec_test 结束并且分配了内存数据被释放。不好。

当然,方法 3 完全避免了这种情况,因为您使用这些向量在 Eigen::Map 的生命周期内存储从 get_vec_test 编辑的向量 return。

您看到的问题与 NRVO 无关,与理解 C++ 中的对象生命周期有关。您的代码试图通过指向这些对象的指针对不再存在的对象执行操作。