Exceptional C++: [Item 46 Forwarding Functions] [条款46 转发函数]

条款46 转发函数

难度:3

编写转发函数的最好方式是什么?基本答案很简单,但是我们还是可以学到标准定案之前做出的一个微妙的语言变化。

转发函数是将任务转发给其他函数或对象的有用工具,尤其是在高效完成转发的时候。

评论下面的转发函数。你会修改它吗?如果会,怎样修改?

// file f.cpp
//
#include "f.h"
/*...*/
bool f( X x )
{
  return g( x );
}

回答

还记得这个问题的介绍吗?转发函数是将任务转发给其他函数或对象的有用工具,尤其是在高效完成转发的时候。

这个介绍说到了问题的重点:效率。

有两个主要增强方式使得这个函数更高效。第一个总应被采用;第二个需要权衡。

1.    以const&而不是传值方式传递参数。

这不是很明显的吗?你会问。不,不是,在这个特例里。直到最近1997年之前,C++语言标准草案指出,如果编译器能够证明参数x除了传递给g()以外,不会用于其它目的,编译器可以完全省略x(也就是说,消除不必要的x)。例如,如果客户端代码象下面这样:

X my_x;
f( my_x );

那么编译器被允许按下面任一方式处理:

o    为f()创建一个my_x的拷贝(一个在f()作用域内名为x的变量),然后传递给g()。

o    直接传递my_x给g()而不创建任何拷贝,因为编译器注意到额外的拷贝除了作为传递给g()的参数以外永远都不会被使用。

后者更有效率,对吧?这就是优化编译器所支持的,对吧?

是的是的,直到1997年7月的伦敦会议。在这次会议上,草案经过修订,针对这类允许编译器优化额外拷贝的场景作了更多限制。这个变化是必要的,来避免编译器被允许肆意地消除拷贝构造时所带来的问题,尤其是拷贝构造带有副作用时。在一些合理的场景中,合理的代码可能依赖于一个对象实际上的拷贝数目。

现在,编译器仍然可能消除拷贝构造函数的场景是返回值优化(去你喜爱的书本查看细节)和临时对象。这意味着对于转发函数比如f(),编译器必须完成两次拷贝。(作为f()的作者)我们知道这种情况下额外拷贝是不必要的,我们应退回到我们的常规规则上并将参数x声明为const X&。

方针:尽可能以引用而不是传值来传递对象。

注意:如果我们一直都遵循这个常规规则,而不是试图从编译器被允许做什么这类细节中获取好处,规则的改变不会影响到我们。这是一个有关为什么越简单越好的清晰例子:尽量避免语言满是灰尘的角落,并努力不依赖于矫揉造作的微妙之处。

方针:避免语言满是灰尘的角落;采用最简单的技术最有效。

2.    将函数内联化。这里需要权衡。总之,缺省应将所有的函数外联化,然后有选择地内联化你确信需要通过内联提高性能的个别函数。

方针:避免内联或者细节调优,直到性能概要证明确实需要。

如果你将函数内联化,正面影响是你避免了调用函数f()的额外开销。

负面影响是f()的内联化暴露了f()的实现,并使得客户端代码依赖于f()的实现。如果f()发生改变,所有的客户端代码都必须重新编译。更糟的是,客户端代码现在还至少需要知道函数g()的原型声明。这实在是有点丢人,因为客户端代码不会直接调用g(),并且可能从不需要g()的原型声明(至少就我们的例子而言)。而且如果g()改为取用其它类型的参数,客户端代码也会依赖于那些类型的声明。

内联化和非内联化都是合理的选择。这是一个基于收益和弊端的权衡,依赖于你所了解的,f()现在被多么广泛的使用,以及将来它会多么频繁的变化。

Exceptional C++: [Item 46 Forwarding Functions] [条款46 转发函数],布布扣,bubuko.com

时间: 2024-10-13 16:19:11

Exceptional C++: [Item 46 Forwarding Functions] [条款46 转发函数]的相关文章

Exceptional C++: [Item 47. Control Flow] [条款47 控制流]

条款47控制流 难度:6 你到底有多了解C++代码的执行顺序呢?通过这个问题来测试一下你的知识. "恶魔藏在细节里."尽量指出下面(人为)代码的问题,请集中在控制流相关的问题上. #include <cassert> #include <iostream> #include <typeinfo> #include <string> using namespace std; // The following lines come from

Effective C++笔记_条款46 需要类型转换时请为模板定义非成员函数

看这个章节的时候又跑回去看了一下 条款 24,本章的例子就是基于下面这个例子而来的.在 item 24 中,支持混合运算的示例代码如下: 1 class Rational { 2 public: 3 Rational(int numerator = 0, int denominator = 1); 4 int mumerator() const; 5 int denominator() const; 6 private: 7 int numerator; 8 int denominator; 9

Effective C++ 条款46 需要类型转换时请为模板定义非成员函数

1. 条款24举出一个Rational的例子,来说明为什么只有non-member函数才有能力"在所有实参身上实施隐式类型转换".Rational的定义如下: class Rational{ public: Rational(int numerator=0,int denominator=1); int numerator()const; int denominator()const; private: int numerator; int denominator; }; operat

Effective C++ Item 46 当需要投你非成员函数定义模板

本文senlie原版的,转载请保留此地址:http://blog.csdn.net/zhengsenlie 经验:当我们编写一个 class template, 而它所提供之"与此 template 相关的"函数支持"全部參数之隐式类型转换"时.请将那些函数定义为 "class template内部的 friend 函数". 演示样例: template<typename T> class Rational{ public: Rati

Effective C++ 条款46

本节条款:需要类型转换时请为模板定义非成员函数 这节知识是在条款24的基础上,讲述的有关非成员函数在模板类中(non-member function template)的作用. 我们先看一下条款24讲述的知识核心.条款24讲述了我们如何能实现类的对象在特定条件下的隐式转换问题. 我们先看以下代码: ** 例一: ** #include<iostream> #include<assert.h> using namespace std; class Rational { private

条款46:需要类型转换的时候请为模板定义非成员函数

看看下面这个例子: 1 template<typename T> 2 class Rational{ 3 public: 4 Rational(const T & numerator, const T & denominator); 5 const T numerator()const; 6 const T denominator() const; 7 }; 8 template<typename T> 9 const Rational<T> opera

Execution Order of Event Functions, unity 3d 事件函数的执行顺序

学习unity3d,感觉事件顺序很重要.就翻译一下官方文档吧. Execution Order of Event Functions 事件函数的执行顺序 In Unity scripting, there are a number of event functions that get executed in a predetermined order as a script executes. This execution order is described below: Unity 脚本中

BFD (Bidirectional Forwarding Dectection,双向转发检测)

BFD是Bidirectional Forwarding Dectection的缩写,它是一个用于检测两个转发点之间故障的网络协议. BFD是一种双向转发检测机制,可以提供毫秒级的检测,可以实现链路的快速检测,BFD通过与上层路由协议联动,可以实现路由的快速收敛,确保业务的永续性. BFD Echo保文采用UDP封装,目的端口号为3784,源端口号在49152到65535的范围内.目的IP地址为发送接口的地址,源IP地址由配置产生,(需要注意的是:配置的源IP地址要避免产生ICMP重定向). 那

More Effective C++ 条款31 让函数根据一个以上的对象类型来决定如何虚化

1. 假设要编写一个发生在太空的游戏,其中有飞船(spaceship),太空站(space station)和小行星(ssteroid),使它们继承自一个抽象基类GameObject,整个继承体系像这样: class GameObject { ... }; class SpaceShip: public GameObject { ... }; class SpaceStation: public GameObject { ... }; class Asteroid: public GameObj