c ++ eigen 3.5, собственные карты не используют именованную оптимизацию возвращаемого значения? - PullRequest
0 голосов
/ 06 октября 2018

Я довольно новичок в 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;
 }

метод 1:

Я начал с простой передачи значений в функцию, затем в функцию, сохраняя эти значения в стандартном:: 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

Вывод карт явно не тот, чтожелательно.Кроме того, карты не должны указывать на один и тот же вектор.

метод 2:

Затем я попытался вернуть 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 здесь не происходит.

метод 3:

Наконец я попытался вернуть вектор, а затем инициализировать карту в следующей строке:

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

Это дает желаемый результат, и ядумаю NRVO на std :: vector является причиной, почему.

попытка понять результаты:

Как уже упоминалось, я думаю, что определяющим фактором того, дает ли метод желаемые результаты, является то, происходит ли NRVO или нет.

Для третьего метода я знаю, что NRVO происходит для std :: vector.

Для второго метода я вижу, что, возможно, данные в векторе уничтожаются, когда функция выходит так, что одинв вызывающей функции остается «висячий вектор» (поэтому я предполагаю, что NRVO для std :: vector происходит только тогда, когда вы явно устанавливаете функцию return равной вектору того же типа, что и return ??).И поскольку данные были уничтожены, те же адреса могут использоваться для второго вызова функции для хранения двойников (что постигнет та же участь).

Итак, я думаю, что мой главный вопрос (помимо пояснения только что сделанных заявлений): разве NRVO не работает для собственных карт?Есть ли какая-то особая причина для этого?

Я использую eigen 3.5, c ++ 11 с g ++

Заранее спасибо

1 Ответ

0 голосов
/ 06 октября 2018

Если коротко взглянуть на документацию по Eigen::Map, я понимаю, что это не принадлежащая оболочка для данных, на которые указывает указатель, который вы предоставляете.Это позволяет вам выполнять операции с этими данными без необходимости делать дополнительные копии данных.

Глядя на «метод 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);
}

Вы возвращаете 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-х двойных чисел и возвращаете его по значению.Однако затем вы делаете это с ним:

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

Объект std::vector<double>, возвращаемый из get_vec_test, является временным.Вы предоставляете указатель на данные в этом временном в Eigen::Map.В конце оценки полное выражение x_m2 также остается с висящим указателем, поскольку время жизни временного std::vector<double>, возвращенного из get_vec_test, заканчивается, и память, выделенная для данных, освобождается.Не хорошо.

Конечно, метод 3 полностью этого избегает, поскольку вы сохраняете векторы, возвращенные из get_vec_test, для времени жизни Eigen::Map s, используя эти векторы.

Проблема, с которой вы столкнулисьЭто не имеет отношения к NRVO и связано с пониманием времени жизни объектов в C ++.Ваш код пытается выполнить операции над объектами, которые больше не существуют, посредством указателей на эти объекты.

...