[英]Will (N)RVO be applied with my function in this situation?
我有以下代碼:(好吧,實際上它要復雜得多,但為了簡化起見我對其進行了簡化。因此,請不要理會那些看起來很愚蠢的事情。在我的實際情況下我無法更改它們)
#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);
}
重寫的竅門是省略允許跳過復制/移動ctor。 因此,每次我們實際復制一個對象(即使是內聯的)時,我們都會插入一個exit
子句; 只有通過省略才能避免。
除了可能的情況下, GenerateReport
沒有(N)RVO或任何形式的省略。 我懷疑編譯器是否能夠證明這一點,特別是如果字符串是非靜態的並且足夠大以要求堆存儲的話。
對於DoIt
,NRVO和RVO都是可能的。 即使有副作用,Elision也是合法的。
MSVC失敗 -注意到對??0string@@QAE@$QAU0@@Z
調用,這是我的本地string
類的move構造函數。
當我說可能為RVO情況為空時 ,迫使它運行時,您會發現編譯器在這里也無法對RVO進行優化; 在反匯編中有一個內聯的exit(-1)
。
Clang設法RVO return string();
但不是NRVO return val;
。
到目前為止,最簡單的解決方法是:
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的lambda。 對代碼進行零結構更改,並使用C ++ 98編譯器將返回值作為返回值的函數(嗯,它們不支持lambda,但是您知道了)。
我認為(N)RVO在這兩種功能中都不可行。 GenerateReport
必須從字符數組構造一個字符串,NRVO沒有剩余。 DoIt
通過其控制路徑返回兩個不同的值,這使得也無法執行NRVO。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.