在这种情况下,(N)RVO是否适用于我的功能

Will (N)RVO be applied with my function in this situation?

本文关键字:是否 适用于 我的 功能 RVO 这种情况下      更新时间:2023-10-16

我有以下代码:(好吧,实际上它要复杂得多,但我简化了它,使其更容易理解。所以请忽略那些看起来很愚蠢的东西。我无法在实际情况下更改它们)

#include <string>
using std::string;
ReportManager g_report_generator;
struct ReportManager
{
// I know, using c_str in this case is stupid. 
// but just assume that it has to be this way
string GenerateReport() { string report("test"); return report.c_str(); }
}
string DoIt(bool remove_all)
{
if(g_report_generator.isEmpty())
return string();
string val = g_report_generator.GenerateReport();
if(remove_all)
g_report_generator.clear();
return val;
}
void main()
{
string s = DoIt(true);
}

(N)RVO是否适用于我的功能?我做了一些研究,看起来像是这样,但我真的不相信,我想要第二种意见(或更多)。

我正在使用Visual Studio 2017。

为了解决您的问题,我重写了它。

#include <string>

struct string : std::string {
using std::string::string;
string(string&& s) {
exit(-1);
}
string(string const&) {
exit(-2);
}
string() {}
};
struct ReportManager
{
// I know, using c_str in this case is stupid. 
// but just assume that it has to be this way
string GenerateReport()
{
string report("test");
return report.c_str();
}
bool isEmpty() const { return true; }
void clear() const {}
};
ReportManager g_report_generator;
string DoIt(bool remove_all)
{
if(g_report_generator.isEmpty())
return string();
string val = g_report_generator.GenerateReport();
if(remove_all)
g_report_generator.clear();
return val;
}
int main()
{
string s = DoIt(true);
}

这种重写的诀窍在于省略允许跳过复制/移动actor。因此,每次我们实际复制一个对象(即使是内联的)时,我们都会插入一个exit子句;只有省略才能避免它。

GenerateReport不具有(N)RVO或任何类型的省略,除了可能的under as if。我怀疑编译器是否能够证明这一点,尤其是如果字符串是非静态的,并且足够大,需要堆存储。

对于DoIt,NRVO和RVO都是可能的。Elision在那里是合法的,即使有副作用。

MSVC失败--通知调用??0string@@QAE@$QAU0@@Z,它是我的本地string类的移动构造函数。

当我通过说它是空的来强制运行可能的RVO情况时,你会看到编译器在这里也无法进行RVO优化;存在内联到反汇编中的CCD_ 6。

Clang设法RVOreturn string();而不NRVO CCD_。

到目前为止,最简单的解决方案是:

string DoIt(bool remove_all)
{
if(g_report_generator.isEmpty())
return string();
return [&]{   
string val = g_report_generator.GenerateReport();
if(remove_all)
g_report_generator.clear();
return val;
}();
}

具有双RVO的λ和具有简单NRVO的λ。对代码和C++98编译器可以消除返回值的函数进行零结构更改(好吧,它们不支持lambda,但你已经明白了)。

我认为(N)RVO在这两个函数中都不可能。GenerateReport必须从字符数组中构造一个字符串,NRVO已所剩无几。DoIt通过它的控制路径返回两个不同的值,这使得无法执行NRVO。