用函数 return 值初始化一个 std::string,有副本吗?
Initializing a std::string with function return value, is there a copy?
我有以下代码,我使用的是 GCC 和 C++11:
std::string system_call(const char *cmd){
std::string a;
...
return a;
}
std::string st = system_call("whatever code");
那里有隐式副本吗?
我多次调用这个函数,我猜它正在从 system_call
的 return 值复制到变量 st
,然后释放临时 r 值。
有什么办法可以避免被抄袭吗?在编译器中使用 st.swap(system_call())
抛出和错误:
error: no matching function for call to
'std::basic_string::swap(std::string)'
我的问题是:
- 有没有问题
- 如果有的话如何避免
谢谢
编辑:
找到了一种使显式交换工作的方法。但是答案是正确的,没有任何好处,因为编译器已经用 return 值替换了 st,没有任何副本。
system_call("whatever").swap(st);
由于问题被标记为 c++11
,我假设您正在使用 c++11
编译器编译代码。
在这种情况下,不会复制由 system_call
编辑的值 return。编译器可能会使用 return 值移动构造 st
,或使用 Return 值优化来擦除副本。无论哪种方式,都不会有副本。
一般来说,如果 std::string
是在调用中构造的,然后 returned 大多数现代编译器应用 return 值优化([=13= 的特例) ]).如果您想查找精确的规则,您的情况尤其是 Named RVO(感谢@NathanOliver 指出了这一点)。从 C++17 开始,此优化通过标准得到保证。
是否应用优化很难说,但如果没有应用,那么在 C++11 及更高版本中,函数范围内的 std::string
对象很可能会被移动,return 值将被移动到。理论上您可以在 return 值上调用 std::move
,但这样您也可以防止任何 RVO 发生。虽然 move 可能很有效,但 RVO 通常会产生更快的代码,因为根本没有移动或复制任何东西,但对象是在适当的位置构建的,所以我建议不要这样做,而是支持依赖你的编译器。
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
的return值都是同一个对象.
现代编译器会省略,除非你给他们一个病态的标志说 "don't elide",只要标准和代码允许。 "What if it doesn't elide when possible" 就像在问如果编译器将整数加法实现为循环增量会怎样。
两者都是标准允许的,两者都不合理。
当省略失败时(因为您的代码使其不可能),它会返回到 C++11 中的移动语义。对于 std::string
这意味着在移动过程中不会发生内存分配;在小字符串优化的情况下,可能会复制少量字符。
如果您的代码可以 return 沿一条路径的给定命名变量,以及沿另一条路径的不同变量或临时变量,则省略可能会失败。或者,如果您 return 函数参数。
如果您的 return 语句是 return named_variable;
或 return some_temporary_object;
,则允许省略,其中 returned 对象与函数 return 的类型匹配。当您执行 some_type bob = some_temporary;
时也是允许的,其中 some_temporary
是类型 some_type
的临时对象。同样,您可以省略一个函数参数(但不能省略)。
无法"guarantee"省略。
C++17 使得几乎所有上面提到的从临时中删除的情况都是强制性的。
要使省略在 C++14 及之前的版本中起作用,必须有一个复制或移动构造函数。当省略发生时,它不会被调用,但它必须存在。在 C++17 中,当临时对象被 "force" 省略时,不需要存在复制或移动构造函数:临时对象不是一个不同的对象,而是一个表示 "how to construct an object" 的子句,这只会在以后完成。
我有以下代码,我使用的是 GCC 和 C++11:
std::string system_call(const char *cmd){
std::string a;
...
return a;
}
std::string st = system_call("whatever code");
那里有隐式副本吗?
我多次调用这个函数,我猜它正在从 system_call
的 return 值复制到变量 st
,然后释放临时 r 值。
有什么办法可以避免被抄袭吗?在编译器中使用 st.swap(system_call())
抛出和错误:
error: no matching function for call to 'std::basic_string::swap(std::string)'
我的问题是:
- 有没有问题
- 如果有的话如何避免
谢谢
编辑: 找到了一种使显式交换工作的方法。但是答案是正确的,没有任何好处,因为编译器已经用 return 值替换了 st,没有任何副本。
system_call("whatever").swap(st);
由于问题被标记为 c++11
,我假设您正在使用 c++11
编译器编译代码。
在这种情况下,不会复制由 system_call
编辑的值 return。编译器可能会使用 return 值移动构造 st
,或使用 Return 值优化来擦除副本。无论哪种方式,都不会有副本。
一般来说,如果 std::string
是在调用中构造的,然后 returned 大多数现代编译器应用 return 值优化([=13= 的特例) ]).如果您想查找精确的规则,您的情况尤其是 Named RVO(感谢@NathanOliver 指出了这一点)。从 C++17 开始,此优化通过标准得到保证。
是否应用优化很难说,但如果没有应用,那么在 C++11 及更高版本中,函数范围内的 std::string
对象很可能会被移动,return 值将被移动到。理论上您可以在 return 值上调用 std::move
,但这样您也可以防止任何 RVO 发生。虽然 move 可能很有效,但 RVO 通常会产生更快的代码,因为根本没有移动或复制任何东西,但对象是在适当的位置构建的,所以我建议不要这样做,而是支持依赖你的编译器。
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
的return值都是同一个对象.
现代编译器会省略,除非你给他们一个病态的标志说 "don't elide",只要标准和代码允许。 "What if it doesn't elide when possible" 就像在问如果编译器将整数加法实现为循环增量会怎样。
两者都是标准允许的,两者都不合理。
当省略失败时(因为您的代码使其不可能),它会返回到 C++11 中的移动语义。对于 std::string
这意味着在移动过程中不会发生内存分配;在小字符串优化的情况下,可能会复制少量字符。
如果您的代码可以 return 沿一条路径的给定命名变量,以及沿另一条路径的不同变量或临时变量,则省略可能会失败。或者,如果您 return 函数参数。
如果您的 return 语句是 return named_variable;
或 return some_temporary_object;
,则允许省略,其中 returned 对象与函数 return 的类型匹配。当您执行 some_type bob = some_temporary;
时也是允许的,其中 some_temporary
是类型 some_type
的临时对象。同样,您可以省略一个函数参数(但不能省略)。
无法"guarantee"省略。
C++17 使得几乎所有上面提到的从临时中删除的情况都是强制性的。
要使省略在 C++14 及之前的版本中起作用,必须有一个复制或移动构造函数。当省略发生时,它不会被调用,但它必须存在。在 C++17 中,当临时对象被 "force" 省略时,不需要存在复制或移动构造函数:临时对象不是一个不同的对象,而是一个表示 "how to construct an object" 的子句,这只会在以后完成。