使用函数返回值初始化std :: string是否有副本?

时间:2016-09-28 14:38:53

标签: c++ c++11 swap

我有以下代码,我正在使用GCC和C ++ 11:

std::string system_call(const char *cmd){
   std::string a;
   ...
   return a;
}

std::string st = system_call("whatever code");

那里有隐含的副本吗? 我多次调用此函数,我猜它正在从system_call的返回值到变量st进行复制,然后再发布临时r值。

有什么方法可以避免副本吗?在编译器中使用st.swap(system_call()) throws和error:

  

错误:没有匹配的调用函数   '的std :: basic_string的::交换(的std :: string)'

我的问题是:

  1. 如果有问题
  2. 如果有一个
  3. ,如何避免它

    由于

    修改 找到了一种使显式交换工作的方法。但答案正确,没有任何好处,因为编译器已经用返回值替换了st,没有任何副本。

    system_call("whatever").swap(st);
    

3 个答案:

答案 0 :(得分:12)

通常,如果在调用中构造std::string,然后返回大多数现代编译器并应用返回值优化(copy elision的特殊情况)。特别是你的案例是命名的RVO(感谢@NathanOliver指出这一点),如果你想查找精确的规则。从C ++ 17开始,这种优化通过标准得到保证。

是否应用优化很难说,但如果不是,那么在C ++ 11及更高版本中很可能会移动函数范围内的std::string对象从和将返回值移动到。理论上你可以在返回值上调用std::move,但是这样你也可以防止任何RVO发生。虽然移动可能是有效的,但RVO通常会产生更快的代码,因为根本没有移动或复制任何东西,但是对象是在适当的位置构建的,所以我建议不要这样做,并且赞成依赖你的编译器。

答案 1 :(得分:4)

Elision是标准赋予编译器允许多个值共享存在的权限,当它们在代码中看起来是不同的值时。

std::string system_call(const char *cmd){
  std::string a;
  ...
  return a; // all return paths return `a` directly
}

std::string st = system_call("whatever code");

在上述情况下,省略意味着asystem_callst的返回值都是相同的对象

现代编制者除非你给他们一个病态的标志,否则只要标准和代码允许,就不要说“不要”。 "如果在可能的情况下它没有消失怎么办?"就像询问编译器将整数加法实现为循环增量一样。

两者都是标准所允许的,也不合理。

当elision失败时(因为你的代码无法实现),它会回归到C ++ 11中的移动语义。对于std::string,这意味着移动中不会发生内存分配;在小字符串优化的情况下,可以复制少量字符。

如果您的代码可以沿一条路径返回给定的命名变量,而另一条路径返回另一条路径,则Elision可能会失败。或者,如果您返回一个函数参数。

如果您的return语句为return named_variable;return some_temporary_object;,则允许使用Elision,其中返回的对象与函数返回的类型匹配。执行some_type bob = some_temporary;时也允许这样做,其中some_temporarysome_type类型的临时对象。类似地,你可以忽略一个函数参数(但不能忽略它)。

没有办法保证"省音。

C ++ 17使得几乎所有从临时案例中删除的都是强制性的。

对于elision在C ++ 14及以前版本中工作,必须有一个复制或移动构造函数。当elision发生时,它不被调用,但它必须存在。在C ++ 17中,临时工具是强迫性的。除此之外,不存在复制或移动构造函数:临时不是一个独特的对象,而是一个表示"如何构造一个对象的子句",这只能在以后完成。

答案 2 :(得分:3)

由于问题被标记为// change the signature async Task CreateDisposableAsync(){ // use using blocks for anything that needs to be disposed // try/finally is also acceptable using(var someDisposableInstance = new SomethingDisposable()){ // implementation } } ,我假设您正在使用c++11编译器编译代码。

在这种情况下,c++11返回的值不会被复制。编译器可能会使用返回值移动构造system_call,或使用返回值优化来擦除副本。无论哪种方式,都不会有副本。