g ++:如果涉及多个翻译单元,RVO如何工作 [英] g++: How RVO works in case that multiple translation units are involved

查看:230
本文介绍了g ++:如果涉及多个翻译单元,RVO如何工作的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

首先,请查看以下代码,其中包含2个翻译单元。

  --- foo.h --- 

class Foo
{
public:
Foo();
Foo(const Foo& rhs);
void print()const;
private:
std :: string str_;
};

Foo getFoo();

--- foo.cpp ---
#include< iostream>

Foo :: Foo():str _(hello)
{
std :: cout< Default Ctor< std :: endl;
}

Foo :: Foo(const Foo& rhs):str_(rhs.str_)
{
std :: cout< Copy Ctor< std :: endl;
}

void Foo:print()const
{
std :: cout< print [<< str_<< ]<< std:endl;
}

Foo getFoo()
{
return Foo(); //期待RVO
}

--- main.cpp ---
#includefoo.h

int main()
{
Foo foo = getFoo();
foo.print();
}

请确保foo.cpp和main.cpp是不同的翻译单元。根据我的理解,我们可以说,在翻译单元main.o(main.cpp)中没有getFoo()的实现细节。



如果我们编译和执行上面的内容,我看不到Copy Ctor字符串,表示RVO在这里工作。



如果有人愿意让我知道如果'getFoo()'的实现细节没有暴露给翻译单元main.o,这可以实现?



我进行了上述实验通过使用GCC(g ++)4.4.6。

解决方案

编译器只需要一致工作。



换句话说,编译器只需要查看一个返回类型,并且基于该类型,决定返回该类型对象的函数将如何返回值。



至少在典型情况下,这个决定是公平的 。它预留一个寄存器(或者可能两个)用于返回值(例如,在通常是EAX或RAX的Intel / AMD x86 / x64上)。任何类型足够小,以适应,将返回那里。对于任何类型太大以至于不适合,函数将接收一个隐藏的指针/引用参数,告诉它在哪里存放返回结果。注意,这一点在没有涉及RVO / NRVO的情况下非常适用 - 事实上,它同样适用于返回 struct 的C代码,因为它对C ++返回 class 对象。虽然返回一个 struct 可能在C和C ++中并不常见,但它仍然允许,编译器必须能够编译代码。 p>

真的有两个单独的(可能的)副本可以删除。一个是编译器可以在堆栈上分配空间以用于本地保存将是返回值,然后从那里复制到指针在返回期间引用的地方。



第二个是可能从该返回地址复制到值真正需要结束的其他位置。



第一个在函数本身内部被消除,但没有效果在其外部接口。它最终将数据放在隐藏的指针告诉它的地方 - 唯一的问题是它首先创建本地副本,还是总是直接与返回点一起工作。显然用[N] RVO,它总是直接工作。



第二个可能的副本是从那个潜在的临时值到值真正需要结束的地方。这通过优化调用序列而不是函数本身来消除,即,给予函数指向该返回值的最终目的地的指针,而不是到某个临时位置,编译器然后从该临时位置将该值复制到其目的地。


Firstly please take a look at the following code, which consists of 2 translation units.

--- foo.h ---

class Foo
{
public:
    Foo();
    Foo(const Foo& rhs);
    void print() const;
private:
    std::string str_;
};

Foo getFoo();

--- foo.cpp ---
#include <iostream>

Foo::Foo() : str_("hello")
{
    std::cout << "Default Ctor" << std::endl;
}

Foo::Foo(const Foo& rhs) : str_(rhs.str_)
{
    std::cout << "Copy Ctor" << std::endl;
}

void Foo:print() const
{
    std::cout << "print [" << str_ << "]" << std:endl;
}

Foo getFoo()
{
    return Foo(); // Expecting RVO
}

--- main.cpp ---
#include "foo.h"

int main()
{
    Foo foo = getFoo();
    foo.print();
}

Please be sure that foo.cpp and main.cpp are different translation units. So as per my understanding, we can say that there is no implementation details of getFoo() available in the translation unit main.o (main.cpp).

However, if we compile and execute the above, I could not see the "Copy Ctor" string which indicates that RVO works here.

It would be really appreciated if anyone of you kindly let me know how this can be achieved even if the implementation details of 'getFoo()' is not exposed to the translation unit main.o?

I conducted the above experiment by using GCC (g++) 4.4.6.

解决方案

The compiler simply has to work consistently.

In other words, the compiler has to look solely at a return type, and based on that type, decide how a function returning an object of that type will return the value.

At least in a typical case, that decision is fairly trivial. It sets aside a register (or possibly two) to use for return values (e.g., on an Intel/AMD x86/x64 that'll normally be EAX or RAX). Any type small enough to fit into that will be returned there. For any type too large to fit there, the function will receive a hidden pointer/reference parameter that tells it where to deposit the return result. Note that this much applies without RVO/NRVO being involved at all -- in fact, it applies equally to C code that returns a struct as it does to C++ returning a class object. Although returning a struct probably isn't quite as common in C as in C++, it's still allowed, and the compiler has to be able to compile code that does it.

There are really two separate (possible) copies that can be eliminated. One is that the compiler may allocate space on the stack for a local holding what will be the return value, then copy from there to where the pointer refers during the return.

The second is a possible copy from that return address into some other location where the value really needs to end up.

The first gets eliminated inside the function itself, but has no effect on its external interface. It ultimately puts the data wherever the hidden pointer tells it to -- the only question is whether it creates a local copy first, or always works directly with the return point. Obviously with [N]RVO, it always works directly.

The second possible copy is from that (potential) temporary into wherever the value really needs to end up. This is eliminated by optimizing the calling sequence, not the function itself -- i.e., giving the function a pointer to the final destination for that return value, rather than to some temporary location, from which the compiler will then copy the value into its destination.

这篇关于g ++:如果涉及多个翻译单元,RVO如何工作的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆