为什么可以从函数中返回“向量”?


108

请考虑此代码。我已经看过几次这种类型的代码。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;
}

18
返回时将其复制。
songyuanyao 2014年

6
没有人保证..它死,但是在复制之后。
Maroun

7
您只有在函数返回引用的情况下才有问题:std::vector<std::string>&
Caduchon 2014年

14
@songyuanyao不,它将被感动。
2014年

15
@songyuanyao是的。C ++ 11是当前的标准,因此C ++ 11是C ++。
2014年

Answers:


68

我们可以保证它不会死吗?

只要没有引用返回,这样做就很好了。words将被移至接收结果的变量。

局部变量将超出范围。移动(或复制)之后。


2
但是对于可能容纳1000个条目的向量,它的效率或性能有什么问题吗?
zar 2015年

@zadane这是个问题吗?此外,我提到移动,这将避免采取返回值的副本实际上(可至少与目前的标准)。
πάνταῥεῖ

2
并非不是真正的问题,但我正在从那个角度独立地寻找答案。我不知道我是否张贴我的问题,恐怕他们会将其标记为重复:)
zar

@zadane “恐怕他们会将其标记为重复”。请看投票较高的答案。即使对于较旧的实现,您也不必担心,无论如何,大多数编译器都会正确地对其进行优化。
πάνταῥεῖ

107

C ++ 11之前的版本:

该函数将不返回局部变量,而是它的一个副本。但是,您的编译器可能会执行优化,而不执行任何实际的复制操作。

看到 此问题和答案更多详细信息,。

C ++ 11:

该函数将移动值。有关更多详细信息,请参见此答案


2
它将被移动,而不是被复制。这是有保证的。
2014年

1
这也适用于C ++ 10吗?
Tim Meyer 2014年

28
没有C ++ 10这样的东西。
2014年

C ++ 03没有移动语义(但是复制可能已被删除),但是C ++是C ++ 11,问题是关于C ++的。
2014年

19
对于C ++ 11独有的问题,有一个单独的标记。我们中的许多人,特别是大公司中的程序员,仍然坚持使用尚未完全支持C ++ 11的编译器。我将问题更新为对于两个标准都是准确的。
Tim Meyer 2014年

26

我认为您指的是C(和C ++)中的问题,即不允许从函数返回数组(或者至少不能按预期方式工作)-这是因为数组返回将(如果您将其写入,简单形式)返回指向堆栈上实际数组的指针,然后在函数返回时立即将其删除。

但是在这种情况下,它可以工作,因为 std::vector是一个类,并且类(如struct)可以(并且将被)复制到调用者上下文中。[实际上,大多数编译器将使用称为“返回值优化”的东西来优化这种特殊类型的复制,这种特殊引入是为了避免从函数返回大型对象时复制它们,但这是一种优化,从程序员的角度来看,它将的行为就像该对象的赋值构造函数一样]

只要您不返回指针或对函数返回中的内容的引用,就可以了。


13

为了很好地理解行为,可以运行以下代码:

#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()

请注意,此示例在C ++ 03上下文中提供,对于C ++> = 11可以进行改进


1
如果该示例还包括一个移动构造函数和一个移动赋值运算符,而不仅仅是复制构造函数和复制赋值运算符,则该示例将更加完整。(如果不存在移动功能,则将使用副本功能。)
盖伊

@SomeGuy我同意,但是我不使用C ++ 11。我无法提供我所没有的知识。我添加一个便条。随时为C ++> = 11添加答案。:-)
Caduchon

-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 MOP(按参考)
5.09 MOP返回向量

在调试模式下,情况更糟:

参考值0.053 MOPS
返回向量0.034 MOP


-10

这实际上是设计的失败。您不应该将返回值用于不是基本的任何东西,而不是用于相对不重要的任何东西。

理想的解决方案应通过返回参数决定引用/指针并正确使用“ const \'y \'ness”作为描述符来实现。

最重要的是,您应该认识到C和C ++中的数组上的标签实际上是指针,并且其预订实际上是偏移量或加号。

因此,标签或ptr array_ptr ===数组标签,因此返回foo [offset]的确是说在内存指针位置foo的返回元素+类型为return type的偏移量。


4
..........什么。显然,您没有资格提出诸如“设计失败”之类的指控。实际上,RVO和移动操作对价值语义的促进是现代C ++风格主要成功之一。但是您似乎一直在思考原始数组和指针,所以我不希望您能理解这一点。
underscore_d
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.