如果返回参数,是否可以复制省略

Copy elision possible if returning parameter?

本文关键字:复制省 是否 返回 参数 如果      更新时间:2023-10-16

考虑一个函数,该函数按值获取对象,对其执行一些操作并返回该对象,例如:

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是一个充满晦涩优化的野兽。

因此,让我们考虑一个使用ints的示例。调用方具有{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);
}

导螺杆即使调用者可以看到该参数将被返回。析构函数仍然发生了好几次。这是因为标准不支持忽略返回参数的副作用。所以编译器有时可以优化返回参数。