gpt4 book ai didi

c++ - RVO 在失败时强制编译错误

转载 作者:可可西里 更新时间:2023-11-01 16:38:48 28 4
gpt4 key购买 nike

这里有很多关于何时可以完成 RVO 的讨论,但关于何时真正完成的讨论并不多。正如多次声明的那样,RVO 不能根据标准得到保证,但是有没有办法保证 RVO 优化成功或相应代码编译失败?

到目前为止,我部分成功地在 RVO 失败时使代码发出链接错误。为此,我声明了复制构造函数而不定义它们。显然,在我需要实现一个或两个复制构造函数(即 x(x&&)x(x const&))的极少数情况下,这显然既不稳健也不可行。

这引出了我的第二个问题:为什么编译器编写者选择在用户定义的复制构造函数就位时启用 RVO,而不是在仅存在默认复制构造函数时选择启用 RVO?

第三个问题:是否有其他方法可以为普通数据结构启用 RVO?

最后一个问题( promise ):你知道有什么编译器可以让我的测试代码表现得与我用 gcc 和 clang 观察到的不同吗?

这是 gcc 4.6、gcc 4.8 和 clang 3.3 的一些示例代码,显示了问题。该行为不依赖于一般优化或调试设置。当然选项 --no-elide-constructors 会按照它说的去做,即关闭 RVO。

#include <iostream>
using namespace std;

struct x
{
x () { cout << "original x address" << this << endl; }
};
x make_x ()
{
return x();
}

struct y
{
y () { cout << "original y address" << this << endl; }
// Any of the next two constructors will enable RVO even if only
// declared but not defined. Default constructors will not do!
y(y const & rhs);
y(y && rhs);
};
y make_y ()
{
return y();
}

int main ()
{
auto x1 = make_x();
cout << "copy of x address" << &x1 << endl;
auto y1 = make_y();
cout << "copy of y address" << &y1 << endl;
}

输出:

original x address0x7fff8ef01dff
copy of x address0x7fff8ef01e2e
original y address0x7fff8ef01e2f
copy of y address0x7fff8ef01e2f

RVO 似乎也不适用于纯数据结构:

#include <iostream>

using namespace std;

struct x
{
int a;
};

x make_x ()
{
x tmp;
cout << "original x address" << &tmp << endl;
return tmp;
}

int main ()
{
auto x1 = make_x();
cout << "copy of x address" << &x1 << endl;
}

输出:

original x address0x7fffe7bb2320
copy of x address0x7fffe7bb2350

更新:请注意,某些优化很容易与 RVO 混淆。像 make_x 这样的构造函数助手就是一个例子。参见 this example优化实际上是由标准强制执行的。

最佳答案

问题是编译器做了太多的优化:)

首先,我禁用了make_x()的内联,否则我们无法区分RVO和内联。但是,我确实将其余部分放入匿名 namespace ,以便外部链接不会干扰任何其他编译器优化。 (证据表明,外部链接可以防止内联,谁知道还有什么......)我重写了输入输出,现在它使用 printf();否则生成的汇编代码会因为所有 iostream 的东西而变得困惑。所以代码:

#include <cstdio>
using namespace std;

namespace {

struct x {
//int dummy[1024];
x() { printf("original x address %p\n", this); }
};

__attribute__((noinline)) x make_x() {
return x();
}

} // namespace

int main() {
auto x1 = make_x();
printf("copy of x address %p\n", &x1);
}

由于我对gcc生成的汇编的理解非常有限,我和我的一个同事分析了生成的汇编代码。今天晚些时候,我使用 clang 和 -S -emit-llvm 标志来生成 LLVM assembly我个人认为它比 X86 Assembly/GAS Syntax 更好更易读。 .无论使用哪种编译器,结论都是一样的。

我用C++重写了生成的程序集,如果x为空,大概是这样的:

#include <cstdio>
using namespace std;

struct x { };

void make_x() {
x tmp;
printf("original x address %p\n", &tmp);
}

int main() {
x x1;
make_x();
printf("copy of x address %p\n", &x1);
}

如果 x 很大(int dummy[1024]; 成员未注释):

#include <cstdio>
using namespace std;

struct x { int dummy[1024]; };

void make_x(x* x1) {

printf("original x address %p\n", x1);
}

int main() {
x x1;
make_x(&x1);
printf("copy of x address %p\n", &x1);
}

事实证明,如果对象为空,make_x() 只需打印一些有效的、唯一的地址。如果对象为空,make_x() 可以自由地打印一些指向它自己的堆栈的有效地址。也没有什么可复制的,也没有什么可以从 make_x() 返回。

如果你使对象变大(例如添加 int dummy[1024]; 成员),它会在适当的位置构建,因此 RVO 会启动,并且只有对象的地址会传递给make_x() 被打印。没有对象被复制,没有任何东西被移动。

如果对象为空,编译器可以决定不将地址传递给 make_x()(那会是多么浪费资源?:))但让 make_x() 从它自己的堆栈中组成一个唯一的、有效的地址。这种优化发生的时间有些模糊且难以推理(这就是您在 y 中看到的),但这并不重要。

在重要的情况下,RVO 似乎会持续发生。而且,正如我之前的困惑所表明的那样,甚至整个 make_x() 函数都可以内联,因此首先没有要优化掉的返回值。

关于c++ - RVO 在失败时强制编译错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/19262009/

28 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com