Этот код:
#include <vector>
std::vector<float> getstdvec() {
std::vector<float> v(4);
v[0] = 1;
v[1] = 2;
v[2] = 3;
v[3] = 4;
return v;
}
int main() {
std::vector<float> v(4);
for (int i = 0; i != 1000; ++i)
{
v = getstdvec();
}
}
Я неверно понимаю, что функция getstdvec не должна фактически выделять вектор, который она возвращает. Когда я запускаю это в valgrind / callgrind, я вижу 1001 вызов malloc; 1 для начального объявления вектора в main и 1000 для каждой итерации цикла.
Что дает? Как я могу вернуть вектор (или любой другой объект) из функции, подобной этой, без необходимости каждый раз выделять его?
редактировать: я знаю, я могу просто передать вектор по ссылке. У меня сложилось впечатление, что можно (и даже предпочтительнее) написать такую функцию, которая возвращает объект без ненужного выделения.
Когда вы вызываете функцию, для типа возврата, такого как std::vector<T>
компилятор предоставляет память для возвращаемого объекта. Вызываемая функция отвечает за создание экземпляра, который она возвращает в этом слоте памяти.
RVO / NRVO теперь позволяет компилятору опускать создание локального временного объекта, копировать возвращаемое значение в слоте памяти из него, уничтожать временный объект и, наконец, возвращаться вызывающей стороне. Вместо этого вызываемая функция просто создает локальный объект в памяти возвращаемого слота напрямую, а в конце функции просто возвращает.
С точки зрения вызывающей стороны, это прозрачно: оно обеспечивает память для возвращаемого значения, и когда функция вызывает возвращенный, существует допустимый экземпляр. Теперь вызывающая сторона может использовать этот объект и отвечает за вызов деструктора и последующее освобождение памяти.
Это означает, что RVO / NRVO работает только при вызове функции для создания нового экземпляра, а не при его назначении. Ниже приведен пример использования RVO / NRVO:
std::vector<float> v = getstdvec();
но исходный код использует цикл, и в каждой итерации результат из getstdvec()
должен быть построен, и этот временный v
, Нет никакого способа, которым RVO / NRVO мог бы удалить это.
Вы можете передать его по ссылке … copy elision делает это так, что v = getstdvect () выделяет v (в вашем main) непосредственно v (в вашем getstdvec ()) и пропускает копию, обычно связанную с возвратом по значению, но это НЕ будет пропускать v (4) в вашей функции. Чтобы сделать это, вам нужно взять вектор по ссылке:
#include <vector>
void getstdvec(std::vector<float>& v){
v.resize(4);//will only realocate if v is wrong size
v[0] = 1; v[1] = 2; v[2] = 3; v[3] = 4;
return v;
}
int main() {
std::vector<float> v(4);
for (int i=0; i!=1000;++i)
getstdvec(v);
}
Вы выполняете копирование в вашем цикле, а не копирование. Оптимизация RVO применяется только к построению переменных из возвращаемого значения, а не их присвоению.
Я не могу разобрать реальную проблему, которую вы пытаетесь решить здесь. С более подробной информацией можно было бы дать хороший ответ, который решает основную проблему.
В существующем состоянии, чтобы вернуться из вашей функции таким образом, вам нужно будет создать временный вектор, который будет возвращаться при каждом вызове функции.
Самый простой ответ — передать уже созданный векторный объект в функцию.
std::vector<float> getstdvec(std::vector<float> &myvec){
В этом случае вам не нужно возвращать его так
void getstdvec(std::vector<float> &myvec){
вместо того, чтобы использовать возвращаемое значение, вы можете использовать ссылку:
void getstdvec(std::vector<float> &v)
Который может избежать копирования временного объекта
Как я могу вернуть вектор (или любой другой объект) из функции, подобной этой, без необходимости каждый раз выделять его?
По-вашему, вы объявляете локальный вектор размером 4, поэтому каждый раз, когда вызывается функция, она выделяет память. Если вы имеете в виду, что вы всегда изменяете один и тот же вектор, вы можете вместо этого передать вектор по ссылке.
Например:
void getstdvec(std::vector<float>& vec)
{ //^^
//do something with vec
}
внутри main
, вы объявляете вектор и выделяете пространство как то, что вы сделали. Теперь вы делаете следующее:
for (int i=0; i!=1000;++i)
{ //^^^minor: Don't use magic number in the code like this,
//define a const instead
getstdvec(vec);
}