如果返回参数,是否可以复制省略
Copy elision possible if returning parameter?
考虑一个函数,该函数按值获取对象,对其执行一些操作并返回该对象,例如:
std::string MyToUpper (std::string s)
{
std::transform(s.begin(), s.end(), s.begin(), std::toupper);
return s;
}
现在你用一个临时的调用这个函数:
std::string Myupperstring = MyToUpper("text");
从概念上讲,不需要复制。在这种情况下,现代编译器能够消除所有副本吗?如果没有,是否只有移动?这个案例怎么样:
std::string Mylowerstring("text");
std::string Myupperstring = MyToUpper(std::move(Mylowerstring));
最多可以消除两个概念副本中的一个。如果您将一个临时传递给函数,那么根据C++11 12.8/31:的第三个项目符号,该副本可以被消除
当一个临时类对象。。。将被复制/移动。。。,复制/移动操作可以省略
返回不能被忽略;这只能针对临时变量(根据上面引用的规则)或局部变量,根据第一个项目符号:
在返回语句中。。。当表达式是非易失性自动对象(而不是函数或catch子句参数)。。。复制/移动操作可以省略
在没有省略的情况下,返回值被视为右值,并在可能的情况下移动(这里也是可能的);如果函数参数是右值,则它们会被移动,就像在两个示例中一样。
我不这么认为。有些副本可以,而且可能会已消除,但NVRO无法应用,因为它依赖在与返回相同的位置构造的变量价值除了使用值参数外,参数为由调用者构造,而调用者(通常)看不到参数将被返回,因此无法在正确的位置。
概念上不需要复制。在这种情况下,现代编译器能够消除所有副本吗?
是的,如果函数是内联的,这是可能的但是,我想考虑下面的代码示例,而不是您的代码示例。因为std::string
是一个充满晦涩优化的野兽。
因此,让我们考虑一个使用int
s的示例。调用方具有{1, 2, 3}
,并希望在此基础上创建一个包含{2, 4, 6}
的std::vector<int>
。(这大致类似于在调用者处具有文字"text"
,并希望就地构建包含"TEXT"
的std::string
。)
代码:
#include <cstdio>
#include <vector>
using namespace std;
vector<int> mult(vector<int> v) {
for (int& e : v)
e *= 2;
return v;
}
int main() {
vector<int> v( mult({1, 2, 3}) );
for (int i : v)
printf("%dn", i);
}
如果我用gcc 4.7.2将其编译为:g++ -O3 -fwhole-program -Wall -Wextra -std=c++11 -S file.cpp
,那么在程序集中我正好得到一个std::vector<int>
析构函数调用矢量已创建到位生成的程序集尽可能好。
如果我编译完全相同的代码,但省略了-fwhole-program
标志,则mult()
函数不会内联,并且我会得到对std::vector<int>
的析构函数的两个调用。生成的程序集也比以前的情况差得多。
Clang不知道-fwhole-program
标志,所以我在mult()
:中添加了static
关键字
static vector<int> mult(vector<int> v) { ...
然后它也在适当的位置创建向量。
来自James Kanze的回答:
对于值参数,参数由调用者构造,调用者(通常)看不到参数将被返回,因此无法在正确的位置构造它。
我上面所做的(内联mult()
)使调用者可以看到参数将被返回,实际上,结果是在适当的位置构建的。
我已经尝试过以下代码。
#include <iostream>
auto identity(auto v) {
return v;
}
struct foo {
~foo() {
std::cout << "dtor" << std::endl;
}
};
int main() {
foo f{identity(identity(identity(foo{})))};
identity(1);
}
导螺杆即使调用者可以看到该参数将被返回。析构函数仍然发生了好几次。这是因为标准不支持忽略返回参数的副作用。所以编译器有时可以优化返回参数。
- 是否可以初始化不可复制类型的成员变量(或基类)
- 当有分配器意识的容器被复制/移动时,反弹分配器是否被复制/移走
- C++矢量复制构造函数和赋值运算符是否也复制保留空间?
- 将对象的字节复制到数组并再次复制回来是否安全
- 是否可以将不可复制的成员用作使对象不可复制的替代方法?
- 是否可以/希望创建不可复制的共享指针模拟(以启用weak_ptr跟踪/借用类型语义)?
- 如果这不是类的"复制构造函数",是否可以移动对象?
- 在这种情况下,使用 string_view 是否会导致不必要的字符串复制?
- 为什么复制构造函数不需要检查输入对象是否指向自身?
- 除了 std::vector 之外,是否有一个 std 容器不会复制和销毁作为类的元素?
- 如果我在块中编写字符串文字,是否会从数据部分复制整个字符串数据?
- 如果我也使用复制构造函数并且重载 = 运算符,我是否需要析构函数?
- 是否可以避免在以下代码中复制/移动构造函数的需要?
- std::vector 是否有用于引用的复制构造函数?
- 通过默认复制构造函数比较 C++ 字符串是否会影响性能,原因为何?
- 保证复制 elis 是否适用于函数参数?
- 循环中的复制效率是否低于 memcpy()
- 删除指向不同指针类型的复制指针是否会导致内存泄漏
- 较大的对象复制速度是否较慢(C++)
- 禁止复制操作是否会自动禁止移动操作