提问者:小点点

C++特征3.5,特征映射不使用命名返回值优化?


我对C++和它的特征库很陌生。当从函数返回一个特征映射(指向std::vector),或者从函数返回一个std::vector,然后对其应用特征映射时,我遇到了一些我认为有点奇怪的行为。我认为这与命名返回值优化(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中,并返回指向该向量的特征映射(特征矩阵的)。

然后,我得到了矩阵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

这些地图的输出显然不是所期望的。此外,地图不应该指向同一个矢量。

接下来,我尝试返回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没有在这里发生。

最后,我尝试返回向量,然后在下面一行初始化映射:

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

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

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

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

对于第二种方法,我可以看到,在函数退出时,向量中的数据可能会被销毁,因此在调用函数中留下了一个“悬空向量”(因此,我假设只有当您显式地将函数return设置为与return??相同类型的向量时,才会出现std::vector的NRVO)。并且由于数据被破坏,相同的地址可以用于第二个函数调用来存储双倍(它遭受相同的fate)。

所以我猜我的主要问题(除了澄清我刚才所做的陈述之外)是,NRVO对本征映射不起作用吗?这有什么特别的原因吗?

我用的是本征3.5,C++11和G++

提前致谢


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

您将返回一个,其中指针引用向量分配的内存。当在函数末尾超出作用域时,内存将被释放,但仍在映射当前悬空指针所指向的内存。不好。

方法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倍,并返回它的值。但是,您可以使用它执行以下操作:

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

返回的对象是临时的。在中提供一个指向该临时数据的指针。在计算完整表达式的末尾,还有一个悬空指针,因为从返回的临时的生存期结束,分配给数据的内存被释放。不好。

当然,方法3完全避免了这一点,因为您使用这些向量在返回的向量。

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

相关问题


MySQL Query : SELECT * FROM v9_ask_question WHERE 1=1 AND question regexp '(c++|特征|3.5|特征|映射|不使|命名|返回值|优化)' ORDER BY qid DESC LIMIT 20
MySQL Error : Got error 'repetition-operator operand invalid' from regexp
MySQL Errno : 1139
Message : Got error 'repetition-operator operand invalid' from regexp
Need Help?