Почему нормально возвращать вектор из функции?

Пожалуйста, рассмотрите этот код. Я видел этот тип кода несколько раз. words является локальным вектором. Как можно вернуть его из функции?

Можем ли мы гарантировать, что он не умрет?

 std::vector<std::string> read_file(const std::string& path)
 {
    std::ifstream file("E:\\names.txt");

    if (!file.is_open())
    {
        std::cerr << "Unable to open file" << "\n";
        std::exit(-1);
    }

    std::vector<string> words;//this vector will be returned
    std::string token;

    while (std::getline(file, token, ','))
    {
        words.push_back(token);
    }

    return words;
}

Ответ 1

Можем ли мы гарантировать, что он не умрет?

До тех пор, пока ссылка не будет возвращена, это прекрасно подходит для этого. words будет перенесен в переменную, получающую результат.

Локальная переменная выйдет за пределы области видимости. после его перемещения (или копирования).

Ответ 2

До С++ 11:

Функция не возвращает локальную переменную, а скорее ее копию. Однако ваш компилятор может выполнить оптимизацию, когда не выполняется никакого действительного действия копирования.

Смотрите этот вопрос & ответьте для получения дополнительной информации.

С++ 11:

Функция переместит значение. См. этот ответ для получения дополнительной информации.

Ответ 3

Я думаю, что вы ссылаетесь на проблему в C (и С++), которая возвращает массив из функции, не разрешается (или, по крайней мере, не будет работать так, как ожидалось) - это потому, что возврат массива будет (если вы напишите его в простой форме) верните указатель на фактический массив в стеке, который затем быстро удаляется, когда функция возвращается.

Но в этом случае он работает, потому что std::vector является классом, а классы, такие как structs, могут (и будут) копироваться в контекст вызывающих. [На самом деле, большинство компиляторов оптимизируют этот тип копии с помощью чего-то, называемого "Оптимизация возвращаемого значения", специально введенного, чтобы избежать копирования больших объектов, когда они возвращаются из функции, но что оптимизация и с точки зрения программистов она будет вести себя так, как если бы для объекта был вызван конструктор присваивания]

Пока вы не возвращаете указатель или ссылку на то, что находится внутри возвращаемой функции, вы в порядке.

Ответ 4

Чтобы хорошо понять поведение, вы можете запустить этот код:

#include <iostream>

class MyClass
{
  public:
    MyClass() { std::cout << "run constructor MyClass::MyClass()" << std::endl; }
    ~MyClass() { std::cout << "run destructor MyClass::~MyClass()" << std::endl; }
    MyClass(const MyClass& x) { std::cout << "run copy constructor MyClass::MyClass(const MyClass&)" << std::endl; }
    MyClass& operator = (const MyClass& x) { std::cout << "run assignation MyClass::operator=(const MyClass&)" << std::endl; }
};

MyClass my_function()
{
  std::cout << "run my_function()" << std::endl;
  MyClass a;
  std::cout << "my_function is going to return a..." << std::endl;
  return a;
}

int main(int argc, char** argv)
{
  MyClass b = my_function();

  MyClass c;
  c = my_function();

  return 0;
}

Вывод следующий:

run my_function()
run constructor MyClass::MyClass()
my_function is going to return a...
run constructor MyClass::MyClass()
run my_function()
run constructor MyClass::MyClass()
my_function is going to return a...
run assignation MyClass::operator=(const MyClass&)
run destructor MyClass::~MyClass()
run destructor MyClass::~MyClass()
run destructor MyClass::~MyClass()

Ответ 5

Я не согласен и не рекомендую возвращать vector:

vector <double> vectorial(vector <double> a, vector <double> b)
{
    vector <double> c{ a[1] * b[2] - b[1] * a[2], -a[0] * b[2] + b[0] * a[2], a[0] * b[1] - b[0] * a[1] };
    return c;
}

Это намного быстрее:

void vectorial(vector <double> a, vector <double> b, vector <double> &c)
{
    c[0] = a[1] * b[2] - b[1] * a[2]; c[1] = -a[0] * b[2] + b[0] * a[2]; c[2] = a[0] * b[1] - b[0] * a[1];
}

Я тестировал Visual Studio 2017 со следующими результатами в режиме выпуска:

8.01 СС по ссылке
5.09 СС возвращают вектор

В режиме отладки все гораздо хуже:

0,053 MOPS по ссылке
0,034 СС по возвратному вектору

Ответ 6

Это на самом деле провал дизайна. Вы не должны использовать возвращаемое значение для чего-либо, не являющегося примитивом для чего-либо, что не является относительно тривиальным.

Идеальное решение должно быть реализовано через возвращаемый параметр с решением о ссылке/указателе и надлежащим использованием "const\'y \' ness" в качестве дескриптора.

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

Таким образом, метка или ptr array_ptr === метка массива, возвращающая таким образом foo [offset], действительно говорит о возвращаемом элементе в месте указателя памяти foo + смещение типа возвращаемого типа.