如果返回参数,可以复制省略吗?

时间:2014-02-20 12:28:59

标签: c++ gcc c++11 compiler-construction clang

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

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));

3 个答案:

答案 0 :(得分:4)

最多可以省略两个概念副本中的一个。如果你将一个临时函数传递给函数,那么根据C ++ 11 12.8 / 31的第三个项目符号可以省略该副本:

  

当复制/移动临时类对象...时,可以省略复制/移动操作

回报不能被遗漏;根据第一个项目符号,只能对临时工(按照上面引用的规则)或局部变量进行:

  

在return语句中...当表达式是a的名称时   非易失性自动对象(除了函数或catch子句参数)...可以省略复制/移动操作

如果没有elision,返回值将被视为 rvalues 并在可能的情况下移动(并且可以在此处移动);如果函数参数是 rvalues ,则它们会被移动,就像它们在你的例子中一样。

答案 1 :(得分:3)

我不这么认为。一些副本可以,也可能是 被淘汰,但NVRO无法申请,因为它依赖于 变量与返回的位置相同 值。除了带有值参数,参数是 由呼叫者构建,谁看不到(一般)那个 参数将被返回,因此无法在其中构造它 正确的地方。

答案 2 :(得分:1)

  

从概念上讲,不需要复制。在这种情况下,现代编译器是否能够忽略所有副本?

是的,如果函数是内联的,则可以。但是,我想考虑下面的代码示例而不是你的代码示例,因为std::string是一个充满晦涩优化的野性野兽

因此,让我们考虑使用int s的示例。来电者有{1, 2, 3},并希望从中创建包含std::vector<int>的{​​{1}}。 (这大致类似于在调用者处拥有文字 {2, 4, 6}并希望就地构建包含"text"的{​​{1}}。)

代码:

std::string

如果我使用gcc 4.7.2将其编译为:"TEXT",我在程序集中只得到一个#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("%d\n", i); } 析构函数调用。 矢量是就地创建的。生成的程序集尽可能好。

如果我编译完全相同的代码但省略了g++ -O3 -fwhole-program -Wall -Wextra -std=c++11 -S file.cpp标志,则std::vector<int>函数不会被内联,我会得到两次对-fwhole-program的析构函数的调用。生成的程序集也比前一种情况更糟糕。

Clang不知道mult()标记,因此我将std::vector<int>关键字添加到-fwhole-program

static

然后它也就地创建了矢量。


From James Kanze's answer:

  

使用值参数,参数由调用者构造,谁是   看不到(通常)将返回参数,等等   不能在正确的地方建造它。

我上面做的(内联mult())使得调用者可以看到该参数将被返回并且实际上,结果是就地构建的。