簡體   English   中英

在這種情況下,(N)RVO是否可以與我的功能一起使用?

[英]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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM