当“by-value”参数也是“by-value”返回参数时,为什么不能进行复制省略?[复制]

Why cant copy elision take place when a 'by-value' parameter is also the 'by-value' return argument? [duplicate]

提问人:EL IAS 提问时间:9/13/2023 最后编辑:Jan SchultkeEL IAS 更新时间:9/13/2023 访问量:57

问:

在下面的代码片段中,我的函数尝试将其“by-value”参数作为“by-value”返回值返回。我看了一段视频,上面写着:“这里在物理上不可能进行复制省略......”但我不明白为什么我们不能对函数参数和返回值使用相同的内存地址。

std::string foo(std::string s){
    return s;
}

我试图通过观看视频来理解它,但看不出这在哪里无法进行复制省略。

C++ 复制省略 返回值优化

评论

2赞 HolyBlackCat 9/13/2023
如果函数体不可用,则很难实现。对于 [N]RVO,需要直接在所需的添加处构造对象。因此,如果您这样做了,调用者将需要在调用函数之前以某种方式询问函数,它是否要返回您给它的相同对象(因此调用者可以直接从 进行构造),我认为没有一种固定的方法这样做。但我不明白为什么这在理论上是不可能的。std::string a = foo("42");a"42"
1赞 Ahmed AEK 9/13/2023
参数“构造”由调用者完成,而返回“构造”由被调用者完成,调用者不能知道它不需要分配参数,因为它将被返回,但编译器应该只是将调用者分配的参数移动到返回槽中,即:一个简单的 memcpy,假设函数没有被编译器内联。
0赞 273K 9/13/2023
使用通常的默认调用约定,由调用方构造和销毁,并且它无法知道是否返回,并且不得销毁。std::string ss
0赞 EL IAS 9/13/2023
我得到该参数是调用方作用域中的局部变量,因此在调用方函数的堆栈帧中,其内存地址低于调用方函数的基指针。当我再次调用另一个函数时,局部变量和返回值具有不同的内存地址(基指针上方和下方),但这些地址应该只保存指向变量实际内容所在的其他内存地址,这些地址不能都一样吗?
0赞 Ahmed AEK 9/13/2023
这可能会发生,但随后您将使用仅具有引用语义的 python 或 java,最终结果相当于编译器无论如何都会执行的 c++ 移动,所以这已经完成了。

答:

0赞 Jan Schultke 9/13/2023 #1

无论谁负责销毁函数参数,它们都不能是在调用站点创建的结果对象。 这是 C++17 所必需的,并且很难/不可能使用 NRVO(1) 来实现函数参数。 考虑此函数并调用:

std::string foo(std::string s);

std::string r = foo("...");

发生的情况是:

  1. 参数构造自std::string s"..."
  2. 控制权被转移到函数中,函数执行其工作
  3. 按照实现定义的顺序(2):
    • 控制权转移回呼叫者
    • 函数参数被任何拥有控制权的人销毁

如果调用方负责销毁,调用方应该如何知道与 是同一个对象?如果不查看 的定义,就不可能知道,否则它需要获取额外的运行时信息,以通知它与结果对象是哪个参数(如果有)是同一个对象。srsfoo

如果被调用方负责销毁,则意味着不可能,因为被销毁。srssfoo


(1) NRVO 表示命名返回值优化。

(2) 步骤3的相关措辞见[expr.call] p6

是的 实现定义 参数的生存期是在定义参数的函数返回时结束,还是在封闭的完整表达式结束时结束。 每个参数的初始化和销毁发生在出现函数调用的完整表达式的上下文中。