在这种情况下,我的函数会应用 (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);
}
这种重写的诀窍是省略允许跳过 copy/move 构造器。所以每次我们实际复制一个对象时(即使是内联的),我们都会插入一个 exit
子句;只有省略才能避免。
GenerateReport
没有 (N)RVO 或任何类型的省略,除了可能在 as-if 下。我怀疑编译器是否能够证明这一点,特别是如果字符串是非静态的并且大到需要堆存储。
对于DoIt
,NRVO 和RVO 都是可能的。省略在那里是合法的,即使有副作用。
MSVC fails -- 通知调用
??0string@@QAE@$QAU0@@Z
,也就是我本地string
class.
的移动构造函数
当我将可能的 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 编译器可以省略 return 值的函数进行零结构更改(好吧,它们不支持 lambda,但您明白了)。
我认为 (N)RVO 在这两个函数中都不可行。 GenerateReport
必须从字符数组构造一个字符串,NRVO 已经没有任何东西了。 DoIt
returns 两个不同的值通过它控制路径,这使得也无法执行 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);
}
这种重写的诀窍是省略允许跳过 copy/move 构造器。所以每次我们实际复制一个对象时(即使是内联的),我们都会插入一个 exit
子句;只有省略才能避免。
GenerateReport
没有 (N)RVO 或任何类型的省略,除了可能在 as-if 下。我怀疑编译器是否能够证明这一点,特别是如果字符串是非静态的并且大到需要堆存储。
对于DoIt
,NRVO 和RVO 都是可能的。省略在那里是合法的,即使有副作用。
MSVC fails -- 通知调用
??0string@@QAE@$QAU0@@Z
,也就是我本地string
class.
当我将可能的 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 编译器可以省略 return 值的函数进行零结构更改(好吧,它们不支持 lambda,但您明白了)。
我认为 (N)RVO 在这两个函数中都不可行。 GenerateReport
必须从字符数组构造一个字符串,NRVO 已经没有任何东西了。 DoIt
returns 两个不同的值通过它控制路径,这使得也无法执行 NRVO。