是应该通过函数的值还是通过"std :: string&s"作为参数返回std :: string?

Ond*_*tík 1 c++ string performance c++11

为了从函数中返回一个字符串,这两个中的哪一个更有效(即我应该使用哪一个):

std::string f(const std::string& s)
{
    return s + "some text";
}
Run Code Online (Sandbox Code Playgroud)

要么

void f(const std::string& s, std::string &result)
{
    result = s + "some text";
}
Run Code Online (Sandbox Code Playgroud)

我明白答案可能取决于特定的编译器.但我想知道现代C++代码中推荐的方法(如果有的话)是什么.

根据下面的"轨道轻度竞赛"评论,这里是我问到这个问题之前在stackoverflow上找到的一些相关问题:

将const std :: string&作为参数传递的日子是多少?

通过Value或Reference传递std :: string

通过值或const引用?

"std :: string"或"const std :: string&"参数?(该参数在内部复制和修改)

这些都没有回答我关于从函数返回值而不是将字符串作为额外参数返回的特定问题.

Ali*_*Ali 6

让我微观优化你的第二个版本f()并称之为g():

#include <cstdio>
#include <string>
using namespace std;

string f(const string& s) {
    return s + "some text";
}

void g(const string& s, string &result) {
    result.clear();
    result += s;
    result += "some text";
}
Run Code Online (Sandbox Code Playgroud)

现在,让我们将"按价值返回f()" 方法与"超出参数"方法进行比较g().

按价值返回:

int main(int argc, char* argv[]) {

    string s(argv[1]);

    for (int i=0; i<10; ++i) {

      string temp = f(s); // at least 1 memory allocation in each iteration, ouch!

      fprintf(stderr, "%s\n", temp.c_str());
    }
}
Run Code Online (Sandbox Code Playgroud)

在每次迭代中,都有一个内存分配.分配总数将是迭代次数+ 1,即在这种情况下为11.

"超出参数"的方法:

int main(int argc, char* argv[]) {

    string s(argv[1]);

    string temp; // note that this time, it is outside the loop

    for (int i=0; i<10; ++i) {

      g(s, temp);

      fprintf(stderr, "%s\n", temp.c_str());
    }
}
Run Code Online (Sandbox Code Playgroud)

在这种情况下,您将获得3个内存分配(假设缓冲区temp不需要在循环内重新分配),即使您迭代1000000次!这是对价值回报方法的重大改进.

按值返回并依赖于copy-elision或移动语义是一个很好的建议,但正如示例所示,有些情况下out-parameter方法获胜(例如,当您可以重用缓冲区时).

out-parameters的危险在于,在调用站点,只需通过查看代码就可以明显看出函数正在修改它的一些参数.函数的名称必须强烈暗示它正在改变它的一些参数.否则你会得到令人惊讶的结果...... :(

如果你发现这个例子太扭曲了,那么它不是:想想std::getline()!

对于那些认为它是过早优化的人:如果std::getline()它肯定不是!如果将文件的行推入a std::vector并为每行分配一个新的字符串,它将比out-paramter方法慢16倍(使用80字节的行).这听起来很疯狂,因为文件IO应该是瓶颈,但事实并非如此,这是不必要的内存分配.有关详细信息,请参阅Andrei Alexandrescu:使用C++编写快速代码,快速约48分钟.


更新:

  1. R. Martinho Fernandes在评论中明确指出,他用gcc测量的结果与我的结果相矛盾,但与我对clang和libc ++的说法一致; 见 GCCClang.

  2. 在他指出这些之后,我对Andrei Alexandrescu的例子进行了测量.目前,我无法重现他的结果; 它需要进一步分析,以了解引擎盖下发生的事情.

请耐心等待,给我一些时间来澄清不一致之处.

这个故事的外卖总是要衡量.确实测量了答案中提到的内存分配数量,这仍然可以(至少在我的机器上).