|
范围广阔啊。 1、地图 从A到B,哪条路花费最少 / 哪条是最快的路线,如果身上只能花N$,那么应该选择哪条路? 2、超文本 图处理算法是搜索引擎的基本组成部分 3、电路 如“能否将此电路做在芯片上而不出现任何线路交叉” 4、调度 如何满足给定约束,又节省时间 5、事务 如对通信线路的布线从而高效地处理通信;对市场购销现金流的监测以便加强对市场实际情况的了解。 6、匹配 如应聘人员与单位机构的匹配 7、网络 计算机网络的维护,如何调整节点以便确保某些站点或连接不至于处于太“要害”的地位。 8、程序结构 如何最佳地为程序分配资源以便做到最高效?
值得研究。
书里面说的这个词: 型别计算的边界标记
NullType只有声明没有定义。
class NullType; 这是为了表达“我不是个令人感兴趣的型别”,可以作为“找不到型别”的消息标记。类似\0这样。 EmptyType,就是一个空类
这是可被继承的合法型别,可以作为template的缺省参数型别。
在编译时刻,在Conversion类中产生两个常数(编译器帮忙计算)
template <class T,class U> class Conversion { // public: enum { exists2Way = exists && Conversion<U,T>::exists }; enum { sameType = false }; }; 一个是 exists2Way,表示是否可以两个类型互相转换, sameType 表示 T和U是否同一个类型。 不过,虽然书里这么说,我怎么都搞不懂为什么这样可以,测试也是不对的,难道这个sameType的写法还有别的奥妙? 不过下面这个偏特的写法倒是比较容易理解:
template <class T> class Conversion<T,T> { public: enum { exists = 1,exists2Way = 1,sameType = 1 }; }; 这个测试是OK的。 有了这几个常数,要决定两个class之间是否存在继承关系就比较容易了:
#define SUPERSUBCLASS(T,U) \ (Conversion<const U*, const T*>::exists && \ !Conversion<const T*, const void*>::sameType) 如果U是public继承自T,或者T和U是同一个类,那么SUPERSUBCLASS(T,U)传回true,这里是把某个class视为自己的超类,更严谨的做法是:
#define SUPERSUBCLASS_STRICT(T,U) \ (SUPERSUBCLASS(T,U) && \ !Conversion<const T, const U>::sameType) 即排除T与U是同一个类型的情况。 另外,加上 const 是为了 防止因为 const 而导致转型失败,对于已经是const的东西再const一次的话后面一次的const会忽略掉。 再,这个宏的名字很清晰,就是 超类--子类, 前面那个T是超类,U是子类,这个命名比 INHERITS要好。
简而言之:explicit修饰的构造函数不能担任转换函数
这个 《ANSI/ISO C++ Professional Programmer's Handbook 》是这样说的
explicit Constructors A constructor that takes a single argument is, by default, an implicit conversion operator, which converts its argument to an object of its class (see also Chapter 3, "Operator Overloading"). Examine the following concrete example: class string { private: int size; int capacity; char *buff; public: string(); string(int size); // constructor and implicit conversion operator string(const char *); // constructor and implicit conversion operator ~string(); }; Class string has three constructors: a default constructor, a constructor that takes int, and a constructor that constructs a string from const char *. The second constructor is used to create an empty string object with an initial preallocated buffer at the specified size. However, in the case of class string, the automatic conversion is dubious. Converting an int into a string object doesn't make sense, although this is exactly what this constructor does.
Consider the following: int main() { string s = "hello"; //OK, convert a C-string into a string object int ns = 0; s = 1; // 1 oops, programmer intended to write ns = 1, } In the expression s= 1;, the programmer simply mistyped the name of the variable ns, typing s instead. Normally, the compiler detects the incompatible types and issues an error message. However, before ruling it out, the compiler first searches for a user-defined conversion that allows this expression; indeed, it finds the constructor that takes int. Consequently, the compiler interprets the expression s= 1; as if the programmer had written s = string(1); You might encounter a similar problem when calling a function that takes a string argument. The following example can either be a cryptic coding style or simply a programmer's typographical error. However, due to the implicit conversion constructor of class string, it will pass unnoticed: int f(string s); int main() { f(1); // without a an explicit constructor, //this call is expanded into: f ( string(1) ); //was that intentional or merely a programmer's typo? } 'In order to avoid such implicit conversions, a constructor that takes one argument needs to be declared explicit: class string { //... public: explicit string(int size); // block implicit conversion string(const char *); //implicit conversion ~string(); }; An explicit constructor does not behave as an implicit conversion operator, which enables the compiler to catch the typographical error this time: int main() { string s = "hello"; //OK, convert a C-string into a string object int ns = 0; s = 1; // compile time error ; this time the compiler catches the typo } Why aren't all constructors automatically declared explicit? Under some conditions, the automatic type conversion is useful and well behaved. A good example of this is the third constructor of string: string(const char *);
The implicit type conversion of const char * to a string object enables its users to write the following: string s; s = "Hello"; The compiler implicitly transforms this into string s; //pseudo C++ code: s = string ("Hello"); //create a temporary and assign it to s On the other hand, if you declare this constructor explicit, you have to use explicit type conversion: class string { //... public: explicit string(const char *); }; int main() { string s; s = string("Hello"); //explicit conversion now required return 0; } Extensive amounts of legacy C++ code rely on the implicit conversion of constructors. The C++ Standardization committee was aware of that. In order to not make existing code break, the implicit conversion was retained. However, a new keyword, explicit, was introduced to the languageto enable the programmer to block the implicit conversion when it is undesirable. As a rule, a constructor that can be invoked with a single argument needs to be declared explicit. When the implicit type conversion is intentional and well behaved, the constructor can be used as an implicit conversion operator.
就是这样一个结构:
template <typename T> struct Type2Type { typedef T OriginalType; }; 假定有个片断如下,创建一个T*
template <class T,class U> T* Create(const U& arg) { return new T(arg); } 如果对于某个类如“Widget”,其ctor要有两个参数,比如第二个参数必须是-1(对于旧的代码来说,谁知道呢:)),但又不想另外创建一个“CreateWidget”方法,那么怎么办呢,函数是不能偏特化的,即如下代码:
在 VC7下会报告:非法使用显式模板参数 只能使用函数重载,比如:
template <class T,class U> T* Create(const U&arg,T /**//*虚拟*/) { return new T(arg); }
template <class U> Widget * Create(const U& arg, Widget /**//*虚拟*/) { return new Widget(arg,-1); }
这样是可以解决问题,但最大的毛病在于运行时构造了 未被使用的对象这个开销(虚拟的Widget参数)。这时 Type2Type 这个咚咚出场了,按照书的说法,这是“一个型别代表物、一个可以让你传给重载函数的轻量级ID”,如下:
template <class T,class U> T* Create(const U& arg,Type2Type<T>) { return new T(arg); }
template <class U> Widget * Create(const U& arg,Type2Type<Widget>) { return new Widget(arg,-1); }
调用方: String *pStr = Create("hello",Type2Type<String>()); Widget *pW = Create(100,Type2Type<Widget>());
关键是,这个东西也是给编译器看的,妙
inline unsigned __int64 GetCycleCount(void) { _asm _emit 0x0F _asm _emit 0x31 } dasm如下:
: inline unsigned __int64 GetCycleCount(void) 7: { 00401070 55 push ebp 00401071 8B EC mov ebp,esp 00401073 83 EC 40 sub esp,40h 00401076 53 push ebx 00401077 56 push esi 00401078 57 push edi 00401079 8D 7D C0 lea edi,[ebp-40h] 0040107C B9 10 00 00 00 mov ecx,10h 00401081 B8 CC CC CC CC mov eax,0CCCCCCCCh 00401086 F3 AB rep stos dword ptr [edi] 8: _asm _emit 0x0F 00401088 0F 31 rdtsc 10: } 0040108A 5F pop edi 0040108B 5E pop esi 0040108C 5B pop ebx 0040108D 83 C4 40 add esp,40h 00401090 3B EC cmp ebp,esp 00401092 E8 19 00 00 00 call __chkesp (004010b0) 00401097 8B E5 mov esp,ebp 00401099 5D pop ebp 0040109A C3 ret
关键就是那个RDTSC指令,即 Read Time Stamp Counter, 结果会保存在EDX:EAX寄存器对中。 Intel的文档是这样说的:
With the Pentium processor, Intel added an additional instruction called RDTSC (Read Time Stamp Counter). This gives software direct access to the number of clock counts the processor has experienced since its last power-on or hardware reset. With contemporary clock rates of, for example, 3.06 Ghz, that results in a timing period of only 0.326 nanoseconds.
|