为啥可以从函数返回“向量”?

Posted

技术标签:

【中文标题】为啥可以从函数返回“向量”?【英文标题】:Why is it OK to return a 'vector' from a function?为什么可以从函数返回“向量”? 【发布时间】:2014-05-04 12:06:49 【问题描述】:

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

【问题讨论】:

返回时被复制。 没有人保证......它死,但在被复制之后。 只有在函数返回引用时才会出现问题:std::vector&lt;std::string&gt;&amp; @songyuanyao 不会,会搬家的。 @songyuanyao 是的。 C++11 是当前的标准,所以 C++11 就是 C++。 【参考方案1】:

C++11 前:

该函数不会返回局部变量,而是返回它的副本。但是,您的编译器可能会在没有进行实际复制操作的情况下执行优化。

更多详情请参阅this question & answer。

C++11:

函数将移动值。详情请见this answer。

【讨论】:

它将被移动,而不是复制。这是有保证的。 这是否也适用于 C++10? 没有 C++10 这样的东西。 C++03 没有移动语义(但可能已经省略了复制),但 C++ 是 C++11,问题是关于 C++。 C++11 独有的问题有一个单独的标签。我们中的许多人,尤其是大公司的程序员,仍然坚持使用尚未完全支持 C++11 的编译器。我更新了问题,使其对这两个标准都准确。【参考方案2】:

我们能保证它不会死吗?

只要没有返回引用,这样做就完全没问题。 words 将被移动到接收结果的变量中。

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

【讨论】:

但是对于可能包含 1000 个条目的向量来说,效率高还是有任何性能问题? @zadane 有问题吗?我还提到了 moving 这将避免实际获取返回值的副本(至少在当前标准下可用)。 不,不是真正的问题,但我正在独立地从这个角度寻找答案。我不知道我是否发布了我的问题,恐怕他们会将其标记为重复 :) @zadane “我担心他们会将其标记为重复” 很可能。看看higher voted answer。即使对于较旧的实现,您也不用担心,这些编译器无论如何都会正确优化它们。【参考方案3】:

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

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

只要你不返回一个指针或对函数返回的东西的引用,你就可以了。

【讨论】:

【参考方案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()

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

【讨论】:

这个例子如果包含一个移动构造函数和一个移动赋值运算符,而不仅仅是复制构造函数和复制赋值运算符,会更完整。 (如果移动功能不存在,将使用复制功能。) @SomeGuy 我同意,但我不使用 C++11。我无法提供我没有的知识。我添加一个注释。随意添加 C++ >= 11 的答案。:-)【参考方案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 MOPs 返回向量

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

0.053 MOPS 参考 返回向量 0.034 MOPs

【讨论】:

【参考方案6】:

这实际上是设计的失败。您不应该将返回值用于任何不是相对微不足道的东西的原始值。

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

除此之外,您应该意识到 C 和 C++ 中数组上的标签实际上是一个指针,它的订阅实际上是一个偏移量或加法符号。

因此返回 foo[offset] 的标签或 ptr array_ptr === 数组标签实际上是在内存指针位置 foo + 类型返回类型的偏移量处返回元素。

【讨论】:

.......什么。很明显,您没有资格提出诸如“设计失败”之类的指责。事实上,RVO 和移动操作对值语义的提升是现代 C++ 风格的 主要成功之一。但是您似乎一直在思考原始数组和指针,所以我不希望您掌握这一点。

以上是关于为啥可以从函数返回“向量”?的主要内容,如果未能解决你的问题,请参考以下文章

为啥在向量类中实现 operator= 时返回 const 引用

为啥我不能在使用 sort_by_key 对向量进行排序时使用返回引用的键函数?

为啥 Date 以“double”类型返回?

如何从函数返回向量引用?

为啥我不能返回这个向量向量?

从函数 C++ 返回向量