我有以下代码,我正在使用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)'
我的问题是:
由于
修改 找到了一种使显式交换工作的方法。但答案正确,没有任何好处,因为编译器已经用返回值替换了st,没有任何副本。
system_call("whatever").swap(st);
答案 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");
在上述情况下,省略意味着a
,system_call
和st
的返回值都是相同的对象。
现代编制者除非你给他们一个病态的标志,否则只要标准和代码允许,就不要说“不要”。 "如果在可能的情况下它没有消失怎么办?"就像询问编译器将整数加法实现为循环增量一样。
两者都是标准所允许的,也不合理。
当elision失败时(因为你的代码无法实现),它会回归到C ++ 11中的移动语义。对于std::string
,这意味着移动中不会发生内存分配;在小字符串优化的情况下,可以复制少量字符。
如果您的代码可以沿一条路径返回给定的命名变量,而另一条路径返回另一条路径,则Elision可能会失败。或者,如果您返回一个函数参数。
如果您的return语句为return named_variable;
或return some_temporary_object;
,则允许使用Elision,其中返回的对象与函数返回的类型匹配。执行some_type bob = some_temporary;
时也允许这样做,其中some_temporary
是some_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
,或使用返回值优化来擦除副本。无论哪种方式,都不会有副本。