gpt4 book ai didi

c++ - 可以优化从临时对象创建复合对象吗?

转载 作者:塔克拉玛干 更新时间:2023-11-03 01:32:07 26 4
gpt4 key购买 nike

我已经问了几个涉及这个问题的问题,但我得到了不同的回答,所以我认为最好直接问。

假设我们有以下代码:

// Silly examples of A and B, don't take so seriously, 
// just keep in mind they're big and not dynamically allocated.
struct A { int x[1000]; A() { for (int i = 0; i != 1000; ++i) { x[i] = i * 2; } };
struct B { int y[1000]; B() { for (int i = 0; i != 1000; ++i) { y[i] = i * 3; } };

struct C
{
A a;
B b;
};

A create_a() { return A(); }
B create_b() { return B(); }

C create_c(A&& a, B&& b)
{
C c;
c.a = std::move(a);
c.b = std::move(b);
return C;
};

int main()
{
C x = create_c(create_a(), create_b());
}

现在理想情况下 create_c(A&&, B&&) 应该是空操作。调用约定不是创建 A 和 B 并在堆栈上传递对它们的引用,而是应该创建 A 和 B 并按值传递,以代替返回值 c。对于 NRVO,这意味着创建它们并将它们直接传递到 x,而无需函数 create_c 做进一步的工作。

这将避免创建 A 和 B 的拷贝的需要。

是否有任何方法可以允许/鼓励/强制编译器执行此行为,或者优化编译器通常会这样做吗?这仅在编译器内联函数时有效,还是跨编译单元有效。

(我认为这如何跨编译单元工作...)

如果 create_a()create_b() 接受了一个隐藏的参数,即返回值的放置位置,它们可以将结果放入 x 直接,然后通过引用传递给 create_c(),后者什么都不用做并立即返回。

最佳答案

有多种优化代码的方法,但右值引用不是一种。问题是 AB 都不能免费移动,因为你不能窃取物体。考虑以下示例:

template <typename T>
class simple_vector {
typedef T element_type;
typedef element_type* pointer_type;
pointer_type first, last, end_storage;
public:
simple_vector() : first(), last(), end_storage() {}
simple_vector( simple_vector const & rhs ) // not production ready, memory can leak from here!
: first( new element_type[ rhs.last - rhs.first ] ),
last( first + rhs.last-rhs.first ),
end_storage( last )
{
std::copy( rhs.first, rhs.last, first );
}
simple_vector( simple_vector && rhs ) // we can move!
: first( rhs.first ), last( rhs.last ), end_storage( rhs.end_storage )
{
rhs.first = rhs.last = rhs.end_storage = 0;
}
~simple_vector() {
delete [] rhs.first;
}
// rest of operations
};

在这个例子中,由于资源是通过指针保存的,所以有一种简单的方法可以移动对象(即将旧对象的内容窃取到新对象中,并将旧对象留在一个可销毁但无用的状态。只需复制指针并将它们在旧对象中重置为 null,这样原始对象析构函数就不会释放内存。

AB 的问题在于,实际内存是通过数组保存在对象中的,而该数组不能移动到新的 C 对象的不同内存位置。

当然,由于您在代码中使用堆栈分配的对象,编译器可以使用旧的 (N)RVO,当您这样做时:C c = { create_a(), create_b() } ; 编译器可以执行该优化(基本上是在从 create_a 返回的对象的地址上设置属性 c.a,而在编译 create_a,直接在同一地址上创建返回的临时对象,因此有效地 c.a,从 create_a 返回的对象和 create_a 中构造的临时对象(隐含 this 到构造函数)相同的对象,避免了两次复制。同样可以用 c.b 完成,避免了复制成本。如果编译器确实内联您的代码,它将删除 create_c 并将其替换为类似于以下内容的结构:C c = {create_a(), create_b()}; 所以它可以优化所有拷贝。

另一方面请注意,这种优化不能完全用于 C 对象动态分配的情况,如 C* p = new C; p->a = create_a();,由于目的地在栈中,编译器只能优化create_a里面的temporary及其返回值, 但它不能使它与 p->a 重合,因此需要进行复制。这是 rvalue-references 相对于 (N)RVO 的优势,但如前所述,您不能直接在代码示例中有效地使用 rvalue-references

关于c++ - 可以优化从临时对象创建复合对象吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/5865899/

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