我有以下代码:(好吧,实际上它要复杂得多,但为了简化起见我对其进行了简化。因此,请忽略那些看起来很愚蠢的事情。在我的实际情况下我无法更改它们)
#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。
答案 0 :(得分:1)
为解决您的问题,我将其重写了。
#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都是可能的。即使有副作用,省略也是合法的。
MSVC fails-通知对
??0string@@QAE@$QAU0@@Z
,这是我的本地string
类的move构造函数。
当我强制可能的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,但您知道了)。
答案 1 :(得分:1)
我认为(N)RVO在这两种功能中均不可行。 GenerateReport
必须从字符数组构造一个字符串,NRVO没有剩余。 DoIt
通过控制路径返回两个不同的值,这也使得它无法执行NRVO。