c++ - 在这种情况下,我的函数会应用 (N)RVO 吗?

标签 c++ rvo nrvo

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

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

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

GenerateReport 没有 (N)RVO 或任何类型的省略,除非可能在 as-if 下。我怀疑编译器是否能够证明这一点,尤其是当字符串是非静态字符串并且大到需要堆存储时。

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

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

当我强制运行可能的 RVO 案例时 by saying it is empty ,你会看到编译器在这里也无法进行 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,但您明白了)。

关于c++ - 在这种情况下,我的函数会应用 (N)RVO 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/54333687/

相关文章:

c++ - 使用带有 STL 算法的本地类

c++ - 昂贵的移动类型

c++ - 如何确保执行 RVO 而不是复制?

C++:我应该在 return 语句中明确使用 std::move() 来强制移动吗?

c++ - C++ 中结构的奇怪行为 [第 1 部分]

c++ - 在没有lambda函数的情况下使用C++中的另一个 vector 对 vector 进行排序

c++ - 如何更改从多个源文件编译的 g++ 生成的 Linux 共享库的入口点?

c++ - 优化是否应用于单行函数?

C++ 返回对象拷贝

c++ - 引入新变量是否会破坏返回值优化?