预返回优化 [英] pre return optimization

查看:52
本文介绍了预返回优化的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

考虑:


struct memory_pig {//一个非常大的类型:


memory_pig(){

std :: cout<<" mem pig default \ n" ;;

//等等......

};


memory_pig(memory_pig const&){

std :: cout<<" mem pig copy \ n";

//等等...

};


~memory_pig(){

std :: cout<<" mem pig finish\\\
;;

//等...

};


//等等......


}; /// struct memory_pig


memory_pig foo(){

memory_pig结果;

结果=某事;

//等等......

result = something_else;

返回结果;

};


任何时候''foo''被调用输出将包含以下

序列:

mem pig default

mem pig copy

mem pig完成


最后一行输出可能会根据resu重复这是

存储(rvo)或不存在。

因此,将构建两个大型对象,并且至少有一个是 destructed在PASCAL中每次打电话给''foo''你都可以写:


函数foo:memory_pig

开始

foo: =某事;

{等等......}

foo:= somthing_else;

结束


,你可以在函数内部引用返回的对象和

减少复制大对象的开销.C ++缺乏这样的语法

和恕我直言我们应该能够标记结果对象作为引用

实际的回报,这样就不需要额外的副本

构造;这在处理

时非常有用运算符定义。我们可以将return本身声明为object.I

建议使用以下语法:


class ret_type funxn(paramlist){

ret_type return / * optional:* /(initiallizer params);


//等...


if(false)return; //返回类似void函数


//返回内容; //错误:named return不接受参数。


return = something; // ok;

return = something_else;

return.member_funxn ();

another_funcxn(返回);


};


如果''return''是声明为对象{只要你在pharantesis中使用

''return''关键字或通过操作符附带?

引用返回对象:否则无参数''返回''

返回给调用者。

返回对象是函数内的左值,可以别名

以便于阅读:


memory_pig foo(){

memory_pig return,&结果=返回;

结果=某事;

//等等......

结果= something_else;


和每当'foo''被调用时输出如下:


mem pig default


无关紧要的副本/移动已经消失,

暂时的破坏取决于你用它做什么。


问候,

FM。


---

[comp.std.c ++经过审核。要提交文章,请尝试发布]

[您的新闻阅读器。如果失败,请使用mailto:st ***** @ ncar.ucar.edu]

[---请在发布前查看常见问题解答。 ---]

[常见问题: http:/ /www.comeaucomputing.com/csc/faq.html ]

解决方案

2007-10-09 17: 56,terminator(jam)写道:


考虑:


struct memory_pig {//一个非常大的类型:


memory_pig(){

std :: cout<<" mem pig default \ n" ;;

//等......

};


memory_pig(memory_pig const&){

std :: cout<<" mem pig copy \ n" ;;

//等等......

};


~memory_pig(){

std :: cout<<" mem pig finish \ n";

//等等......

};


//等等......


}; /// struct memory_pig


memory_pig foo() {

memory_pig结果;

result = someth荷兰国际集团;



这应该是


memory_pig result = something;





memory_pig结果(某事);


//等等......

result = something_else;

返回结果;

};


任何时候''foo''被称为输出将包含以下

序列:


mem pig默认

mem pig copy

mem pig finish


输出的最后一行可能会根据结果如何存储(rvo)或不存储(rvo)来重复。



通过一些智能编码,您可以将上述内容转换为:


memory_pig foo(){

// ...

返回memory_pig(something,something_else);

}


使用RVO你应该只有一个构造函数调用。


因此,将构造两个大型对象,并且每次调用时至少有一个是
。你可以在PASCAL''foo''写一下:


函数foo:memory_pig

开始

foo:= something;

{等等...}

foo:= somthing_else;

结束


就是你可以引用函数内部返回的对象和

减少复制大对象的开销。



我不熟悉Pascal的确切含义,是不是
类似的东西


void foo(memory_pig& ret){

// ...

ret = something;

// ...

ret = something_else;

}


你似乎忘记的另一件事是对于大型物品

分配也不便宜,有时甚至比
构造函数更贵。


-

Erik Wikstr ?? m


terminator(jam)写道:


考虑:


struct memory_pig {//一个非常大的类型:


memory_pig(){

std :: cout<<" mem pig default \ n" ;;

//等...

};


memory_pig(memory_pig const&){

std :: cout<<" mem pig copy \ n ;;

//等等......

};


~memory_pig(){

std :: cout<<" mem pig finish\\\
" ;;

//等等......

};


// etc ...


}; // / struct memory_pig


memory_pig foo(){

memory_pig result;

result = something ;

//等等......

result = something_else;

返回结果;

};


任何时候''foo''被调用输出将包含以下

序列:


mem pig default

mem pig copy

mem pig finish



我不确定我理解。即使在最糟糕的情况下,我也不会看到在给定时间内存中存在两个以上

memory_pig的副本。鉴于标准对NRVO的明确授权,我希望在大多数实现中,

,从不会超过一个。


输出的最后一行可能会根据结果

的存储(rvo)重复。因此,将构建两个大型

的对象,并且每次调用PASCAL中的''foo''时,至少有一个对象被破坏:$ b $你可以写:


function foo:memory_pig

begin

foo:= something;

{etc ...}

foo:= somthing_else;

end



不能,在Pascal,你必须这样写。


它基本上出现了同样的事情:Pascal

编译器生成一个名为的局部变量foo for

返回;如果名称foo用在

左侧的赋值,则它指的是局部变量,如果它是在右侧使用的

,它指代函数。


当然,在C ++中,与Pascal不同,你可以直接返回

表达式,而无需先将其分配给本地

变量。


你可以在

函数中引用返回的对象并减少开销复制大的

对象。 C ++缺少这样的语法和恕我直言,我们应该能够将结果对象标记为引用实际的返回值

,这样就不需要额外的复制构造了。 />
这在处理

运算符定义时尤其有利。



当前的C ++标准将其留给了

实现,但明确允许它。

C ++中的情况比Pascal更复杂,但是,因为你可以从任何地方返回
,例如:


memory_pig

foo()

{

memory_pig result;

// ...

if(someCondition){

memory_pig aDifferentResult;

// ...

返回aDifferentResult;

}

// ...

返回结果;

}


这是否是一个功能或缺陷可以辩论,但

如果没有

打破相当数量的现有代码,它肯定无法从语言中删除。

-

James Kanze(GABI软件)mailto:ja ********* @ gmail.com

Conseils eninformatiqueorientéeobjet /

Beratung in objektorientierter Datenverarbeitung

9placeSémard,78210 St.-Cyr-l''coco,France,+ 33(0)1 30 23 00 34


10月10日,上午11:18,James Kanze< james.ka ... @ gmail.comwrote:


terminator(jam)写道:


考虑:

struct memory_pig {//一个非常大的类型:

memory_pig(){

std: :cout<<" mem pig default \ n" ;;

//等...

};

memory_pig(memory_pig const&){

std :: cout<<" mem pig copy \ n" ;;

//等等......

};

~memory_pig(){

std :: cout<<" mem pig finish \ n";

/ / etc ...

};

//等等......

}; /// struct memory_pig

memory_pig foo(){

memory_pig结果;

结果=某事;

//等等......

result = something_else;

返回结果;

};

任何时候''foo''被称为输出将包含以下

序列:

mem pig默认

mem pig copy
mem pig finish



我不确定我理解。即使在最糟糕的情况下,我也不会看到在给定时间内存中存在两个以上

memory_pig的副本。鉴于标准对NRVO的明确授权,我希望在大多数实现中,

,从不会超过一个。


输出的最后一行可能会根据结果

的存储(rvo)重复。因此,将构建一个大型的两个物体

,并且每次在PASCAL中调用''foo''时,至少有一个物体被毁坏:你可以写:

函数foo:memory_pig

begin

foo:= something;

{etc ...}

foo:= somthing_else;

结束



不能,在Pascal中,你必须这样写。


它基本上出现了同样的事情:Pascal

编译器生成一个名为foo的局部变量

返回;如果名称foo用在

左侧的赋值,则它指的是局部变量,如果它是在右侧使用的

,它指的是功能。



C ++生成一个未命名的局部变量,它不会被

函数破坏,这个变量是在销毁之前构建的。

其他自动变量,这意味着它不会覆盖任何现有的自动变量,因此必须在[stack]内存的单独

部分中分配。我只是认为可以猜测

''返回''的值是放在

函数的开头,所以可以构造它返回之前参考。

两种返回模式(名称与传统)之间的选择留给程序员


>

当然,在C ++中,与Pascal不同,您可以直接返回

表达式,而无需先将其分配给本地

变量。


你可以参考返回的对象insi de $

函数并减少复制大量
对象的开销。 C ++缺少这样的语法和恕我直言,我们应该能够将结果对象标记为引用实际的返回值

,这样就不需要额外的复制构造了。 />
这在处理

运算符定义时尤其有利。



当前的C ++标准将其留给了

实现,但明确允许它。

C ++中的情况比Pascal更复杂,但是,因为你可以从任何地方返回
,例如:


memory_pig

foo()

{

memory_pig result;

// ...

if(someCondition){

memory_pig aDifferentResult;

// ...

返回aDifferentResult;

}

// ...

返回结果;

}


这是否是一个功能或缺陷可以辩论,但

如果没有

打破相当数量的现有代码,它肯定无法从语言中删除。



我相信它是许多情况下的一个功能,并且在许多其他

案例(操作员功能)中存在缺陷,我并不是要删除任何内容。

现有的语言。我只是想避免使用它econd

构造发生在函数结束时(移动或

复制)。


问候,
FM。


consider:

struct memory_pig{//a really large type:

memory_pig(){
std::cout<<"mem pig default\n";
//etc...
};

memory_pig(memory_pig const&){
std::cout<<"mem pig copy\n";
//etc...
};

~memory_pig(){
std::cout<<"mem pig finish\n";
//etc...
};

//etc...

};///struct memory_pig

memory_pig foo(){
memory_pig result;
result=something;
//etc...
result=something_else;
return result;
};

any time ''foo'' is called the output will contain the following
sequence:
mem pig default
mem pig copy
mem pig finish

the last line of output may repeat based on how the result is
stored(rvo) or not.
So,two objects of a large type will be constructed and at least one is
destructed on every call to ''foo'' in PASCAL you can write:

function foo:memory_pig
begin
foo:=something;
{etc...}
foo:=somthing_else;
end

that is you can refrence the returned object inside the function and
decrease the overhead for copying large objects.C++ lacks such syntax
and IMHO we should be able to mark the result object as referencing
the actual return so that there is no need for the extra copy
construction;this is espesifically beneficall when dealing with
operator definitions .We can declare the return itself as an object.I
suggest the following syntax:

class ret_type funxn (paramlist){
ret_type return /*optional:*/(initiallizer params);

//etc...

if(false)return;//return like void functions

//return something;//error:named return accepts no param.

return=something;//ok;
return=something_else;
return.member_funxn();
another_funcxn(return);

};

Provided that ''return'' is declared as an object { whenever you use the
''return'' keyword inside pharantesis or accompanied via an operator ?
the return object is referenced :otherwise a parameterless ''return''
returns to the caller.
The return object is an lvalue inside the function and can be aliased
for readability:

memory_pig foo(){
memory_pig return , & result=return;
result=something;
//etc...
result=something_else;
};

and whenever ''foo'' is called the output looks like this:

mem pig default

the unnessesary copy/move has vanished and the destruction of
temporary depends on what you do with it.

regards,
FM.

---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:st*****@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.comeaucomputing.com/csc/faq.html ]

解决方案

On 2007-10-09 17:56, terminator(jam) wrote:

consider:

struct memory_pig{//a really large type:

memory_pig(){
std::cout<<"mem pig default\n";
//etc...
};

memory_pig(memory_pig const&){
std::cout<<"mem pig copy\n";
//etc...
};

~memory_pig(){
std::cout<<"mem pig finish\n";
//etc...
};

//etc...

};///struct memory_pig

memory_pig foo(){
memory_pig result;
result=something;

This should be

memory_pig result = something;

or

memory_pig result(something);

//etc...
result=something_else;
return result;
};

any time ''foo'' is called the output will contain the following
sequence:
mem pig default
mem pig copy
mem pig finish

the last line of output may repeat based on how the result is
stored(rvo) or not.


With some smart coding you can transform the above to:

memory_pig foo(){
// ...
return memory_pig(something, something_else);
}

And with RVO you should only have one constructor call.

So,two objects of a large type will be constructed and at least one is
destructed on every call to ''foo'' in PASCAL you can write:

function foo:memory_pig
begin
foo:=something;
{etc...}
foo:=somthing_else;
end

that is you can refrence the returned object inside the function and
decrease the overhead for copying large objects.

I am not familiar with what that exactly means in Pascal, is it
something similar to

void foo(memory_pig& ret) {
// ...
ret = something;
// ...
ret = something_else;
}

Another thing that you seem to forget is that for large objects the
assignment is not cheap either, sometimes even more costly than a
constructor call.

--
Erik Wikstr??m


terminator(jam) wrote:

consider:

struct memory_pig{//a really large type:

memory_pig(){
std::cout<<"mem pig default\n";
//etc...
};

memory_pig(memory_pig const&){
std::cout<<"mem pig copy\n";
//etc...
};

~memory_pig(){
std::cout<<"mem pig finish\n";
//etc...
};

//etc...

};///struct memory_pig

memory_pig foo(){
memory_pig result;
result=something;
//etc...
result=something_else;
return result;
};

any time ''foo'' is called the output will contain the following
sequence:

mem pig default
mem pig copy
mem pig finish

I''m not sure I understand. Even in a worst case scenario, I
can''t see where there would be more than two copies of
memory_pig in memory at a given time. Given the explicit
authorization of NRVO by the standard, I would expect that
in most implementations, there is never more than one.

the last line of output may repeat based on how the result
is stored(rvo) or not. So,two objects of a large type
will be constructed and at least one is destructed on
every call to ''foo'' in PASCAL you can write:

function foo:memory_pig
begin
foo:=something;
{etc...}
foo:=somthing_else;
end

Not can, in Pascal, you have to write it that way.

It basically comes out to the same thing: the Pascal
compiler generates a local variable with the name of foo for
the return; if the name foo is used on the left hand side of
an assignment, it refers to the local variable, and if it is
used on the right hand side, it refers to the function.

And of course, in C++, unlike in Pascal, you can return an
expression directly, without first assigning it to a local
variable.

that is you can refrence the returned object inside the
function and decrease the overhead for copying large
objects. C++ lacks such syntax and IMHO we should be able
to mark the result object as referencing the actual return
so that there is no need for the extra copy construction;
this is espesifically beneficall when dealing with
operator definitions.

The current C++ standard leaves this up to the
implementation, but explicitly allows it. The situation in
C++ is more complicated than in Pascal, however, since you
can return from anywhere, e.g.:

memory_pig
foo()
{
memory_pig result ;
// ...
if ( someCondition ) {
memory_pig aDifferentResult ;
// ...
return aDifferentResult ;
}
// ...
return result ;
}

Whether this is a feature or a defect could be debated, but
it certainly cannot be removed from the language without
breaking a considerable amount of existing code.

--
James Kanze (GABI Software) mailto:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l''école, France, +33 (0)1 30 23 00 34


On Oct 10, 11:18 am, James Kanze <james.ka...@gmail.comwrote:

terminator(jam) wrote:

consider:
struct memory_pig{//a really large type:
memory_pig(){
std::cout<<"mem pig default\n";
//etc...
};
memory_pig(memory_pig const&){
std::cout<<"mem pig copy\n";
//etc...
};
~memory_pig(){
std::cout<<"mem pig finish\n";
//etc...
};
//etc...
};///struct memory_pig
memory_pig foo(){
memory_pig result;
result=something;
//etc...
result=something_else;
return result;
};
any time ''foo'' is called the output will contain the following
sequence:
mem pig default
mem pig copy
mem pig finish


I''m not sure I understand. Even in a worst case scenario, I
can''t see where there would be more than two copies of
memory_pig in memory at a given time. Given the explicit
authorization of NRVO by the standard, I would expect that
in most implementations, there is never more than one.

the last line of output may repeat based on how the result
is stored(rvo) or not. So,two objects of a large type
will be constructed and at least one is destructed on
every call to ''foo'' in PASCAL you can write:
function foo:memory_pig
begin
foo:=something;
{etc...}
foo:=somthing_else;
end


Not can, in Pascal, you have to write it that way.

It basically comes out to the same thing: the Pascal
compiler generates a local variable with the name of foo for
the return; if the name foo is used on the left hand side of
an assignment, it refers to the local variable, and if it is
used on the right hand side, it refers to the function.

C++ generates an unnamed local variable which is not destructed by the
function ,this variable is constructed right before destruction of
other automatic variable which means it does not overwrite any
existing automatic variable ,So it has to be allocated in a separate
portion of [stack]memory.I just think it is posible to guess where the
''return'' value is to be placed right at the beginning of the
function,so it can be constructed and refrenced before returning.
the choice between two modes of returning(name vs traditional) is left
to the programmer.

>
And of course, in C++, unlike in Pascal, you can return an
expression directly, without first assigning it to a local
variable.

that is you can refrence the returned object inside the
function and decrease the overhead for copying large
objects. C++ lacks such syntax and IMHO we should be able
to mark the result object as referencing the actual return
so that there is no need for the extra copy construction;
this is espesifically beneficall when dealing with
operator definitions.


The current C++ standard leaves this up to the
implementation, but explicitly allows it. The situation in
C++ is more complicated than in Pascal, however, since you
can return from anywhere, e.g.:

memory_pig
foo()
{
memory_pig result ;
// ...
if ( someCondition ) {
memory_pig aDifferentResult ;
// ...
return aDifferentResult ;
}
// ...
return result ;
}

Whether this is a feature or a defect could be debated, but
it certainly cannot be removed from the language without
breaking a considerable amount of existing code.

I believe it is a feature for many cases and a defect in many other
cases (operator functions)and I do not mean to remove anything from
the existing language.I just want to make it possible avoid the second
construction that happens at the end of a function (either move or
copy).

regards,
FM.


这篇关于预返回优化的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

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