参数返回值及NRV优化(named return value optimization)

C++11中的移动构造函数又把NRV优化翻出来了,都是采用临时中间值优化,两者不能共存。

参数传递如何实现?

【实现模型1】引入临时对象,使用拷贝构造函数初始化。然后利用bitwise copy将其拷贝到x0的位置。比如:

void foo( X x0 );

X xx;

foo( xx );

改写成

X __temp0;

__temp0.X::X ( xx );

foo( __temp0 );还有一件事需要做,修改foo的声明,可以避免bit-wise copy的那一步。

void foo( X& x0 );

也就是生成一个临时对象,然后调用拷贝构造函数用实参初始化这个临时对象。然后往函数里传递这个临时对象的引用。

【实现模型2】直接在程序栈上的活动记录里进行拷贝构造。

返回值如何实现?

【实现模型1】cfont的实现采用了双阶段转化。1.首先声明一个额外的参数,类型上类对象的引用,用来存放返回结果。2.对这个参数利用返回值进行拷贝初始化。过程类似于参数传递,也是要定义一个临时对象,用来保存返回值,然后在函数内部调用拷贝构造函数用那个return值进行初始化。

X bar()

{

X xx;

// process xx ...

return xx;

}

编译器转化后

// function transformation to reflect

// application of copy constructor

// Pseudo C++ Code

void bar( X& __result )

{

X xx;

// compiler generated invocation

// of default constructor

xx.X::X();

// ... process xx

// compiler generated invocation

// of copy constructor

__result.X::X( xx );

return;

}

【实现模型2】Named Return Value (NRV) optimization,具名返回值优化,实现这种优化有个前提,就是必须提供copy constructor,因为NRV优化的目的就是为了剔除copy constructor的使用。只有有了才能被剔除,否则谈不上剔除。一般的如果不优化NRV,其实现就是类似于模型1中的过程,而实现了优化的过程则上这样的。

X bar()

{

X xx;

// ... process xx

return xx;

}

__result is substituted for xx by the compiler:

void bar( X &__result )

{

// default constructor invocation

// Pseudo C++ Code

__result.X::X();

// ... process in __result directly

return;

}

是否需要拷贝构造函数呢?

【注意】一方面要考虑默认的语义是否符合我们的需要。另一方面如果对象面临大量的拷贝操作,有必要实现一个拷贝构造函数以支持NRV优化。但是如果想使用底层的memcpy之类的直接进行bit wise copy,注意是否真的是bit wise copy拷贝,比如如果是virtual,这样可能破坏调vptr。

Named Return value 优化:

nrv优化的本质是优化掉拷贝构造函数,去掉它不是生成它。当然了,因为为了优化掉它,前提就是它存在,也就是欲先去之,必先有之,这个也就是nrv优化需要有拷贝构造函数存在的原因。 nrv优化会带来副作用,目前也不是正式标准,倒是那个对象模型上举的应用例子看看比较好。极端情况下,不用它的确造成很大的性能损失,知道这个情况就可以了。

为什么必须定义了拷贝构造函数才能进行nrv优化?首先它是lippman在inside c++ object mode里说的。那个预先取之,必先有之的说法只是我的思考。查阅资料,实际上这个可能仅仅只是cfont开启NRV优化的一个开关。

The C++ standard allows the elision of the copy constructor (even if this results in different program behavior), which has a side effect of enabling the compiler to treat both objects as one。也就是我说的副作用,c++标准允许这个副作用的出现,也就是它允许进行NRV优化,但不是必须。

看下vc++8.0的NRV描述:http://msdn.microsoft.com/en-us/library/ms364057(VS.80).aspx

(里面说了在vc++ 8.0才加入了NRV优化,NRV优化的开关是有/O后面的级别开启。g++到底有没有,依上面的结果则是没有nrv优化)

The Visual C++ 8.0 compiler makes use of the flexibility that the standard provides and adds a new feature: Named Return Value Optimization (NRVO). NRVO eliminates the copy constructor and destructor of a stack-based return value. This optimizes out the redundant
copy constructor and destructor calls and thus improves overall performance. It is to be noted that this could lead to different behavior between optimized and non-optimized programs (see the Optimization Side Effects section).

时间: 2024-10-12 10:41:49

参数返回值及NRV优化(named return value optimization)的相关文章

SubSonic3.0使用存储过程查询时,不能使用output参数返回值的问题修改

有个群友问SubSonic3.0执行存储过程时能不能使用output参数返回值,说测试过后获取不到返回值,早上有些时间所以就尝试修改了一下 首先在数据库中创建一个存储过程 1 CREATE PROCEDURE [OutValue] 2 @a int, 3 @b int, 4 @c int output 5 AS 6 Set @c = @a + @b 7 GO 打开Settings.ttinclude模板,找到SPParam类,修改为下面代码 1 public class SPParam{ 2 p

自定义函数中的参数返回值 “-> (Int -> Int)”的问题

func makeIncrementer() -> (Int -> Int) { func addOne(number: Int) -> Int { return 1 + number } return addOne } var increment = makeIncrementer() println(increment(7)) 这里为什么要写两个 Int->Int 这里是返回值是参数,左边是参数,右边是返回值的意思. 自定义函数中的参数返回值 "-> (Int

Python 4.函数参数 返回值与文档查看(基础篇)

本章大纲:(以后文笔格式都会在开头有个大纲) -五大参数- 普通参数 默认参数 关键字参数 收集参数 收集参数解包问题 关键字收集参数 关键字收集参数解包问题 -返回值- -文档查看- -普通参数- 普通参数又名为位置参数,没有默认值,根据具体使用位置进行传值 普通参数和默认参数的不同 普通参数就是如同名字一般普通,没有默认值 而默认参数则可以规定默认值(也就是规定了默认值的普通参数而已) 但是默认参数要牢记一点:默认参数必须指向不变的对象值 请看下面例子 def add_end(L=[]):

命名的返回值优化(Named Return Value optimization (NRVO))

命名的返回值优化: 针对返回一个局部的变量的优化,可以直接用返回的结果对象直接替代局部变量,从而减少了一个复制拷贝,从而提高效率. 比如 一个函数如下: X bar() { X xx; // .. 处理xx return xx; } 而在编译器看来则是如下的代码: // 此处的_result是一个在调用该函数时产生的一个临时对象 // , 然后将该对象传入,用以接受结果 void bar(X & __result) { X xx; // 调用xx的构造函数 xx.X::X(); // .. 处理

Java基础---Java中带参数返回值方法的使用(四十)

Java 中带参带返回值方法的使用 如果方法既包含参数,又带有返回值,我们称为带参带返回值的方法. 例如:下面的代码,定义了一个 show 方法,带有一个参数 name ,方法执行后返回一个 String 类型的结果 调用带参带返回值的方法: 运行结果: 代码: import java.util.Arrays; public class HelloWorld {    public static void main(String[] args) {  HelloWorld hello = new

函数:语法 定义 参数 返回值

函数 我们可以先去定义?个事情或者功能. 等到需要的时候直接去用就好了. 那么这里定义的东?就是一个函数. 函数是对功能或者动作的封装函数: 对代码块和功能的封装和定义 函数的语法和定义 def 函数名(): 函数体 调用: 函数名() 函数的调?: 使用函数名可以调用函数, 写法: 函数名(), 这个时候函数的函数体会被执? 函数的返回执行完函数之后. 我们可以使用return来返回结果 return : 返回 1. 当程序没写过return, 不返回任何结果. 如果你非要接收. 接受到的是N

【编程语言】进程中栈空间的参数返回值以及局部变量的分布

在进程中对于局部变量是怎么分配的,以及函数是怎么调用的其实也就是讲解栈区的具体使用过程.( 下面代码图摘要于网络) 首先,我们要知道,栈中存放的是一个个被调函数所对应的堆栈帧,当函数fun1被调用,则fun1的堆栈帧入栈,fun1返回时,fun1的堆栈帧出栈.什么是堆栈帧呢,堆栈帧其实就是保存被调函数返回时下一条执行指令的指针.主调函数的堆栈帧的指针.主调函数传递给被调函数的实参(如果有的话).被调函数的局部变量等信息的一个结构. 堆栈帧结构如图所示: 首先,我们要说明的是如何区分每个堆栈帧,或

Swift 定义函数 参数 返回值

定义多参数函数 - 用func声明函数  func 函数名 (参数名1: 参数类型, 参数名2: 参数类型) ->  返回类型{ 函数体 } func halfOpenRangeLength(start: Int, end: Int) -> Int { return end - start } let value = halfOpenRangeLength(1, end: 9) print(value)

frida so Hook 函数参数返回值修改

![](https://s1.51cto.com/images/blog/201905/21/e59d78e7f1f9e60f376fd81c75cbd12f.png?x-oss-process=image/watermark,size_16,text_QDUxQ1RP5Y2a5a6i,color_FFFFFF,t_100,g_se,x_10,y_10,shadow_90,type_ZmFuZ3poZW5naGVpdGk=) # -*- coding: UTF-8 -*- import frid