在宏中使得字段只能读取 (几何画板开发笔记 三)

问题提出:
   在研究 emacs lisp 的时候, 文件 lisp.h 里面有一个宏的写法有点特殊, 引起了我的奇怪.

#define XFLOAT_DATA(f)   (0 ? XFLOAT (f)->u.data : XFLOAT (f)->u.data)

其中为了简化问题, 我们将这个宏简写一下:

#define XFLOAT_DATA(f)   (0 ? f->data : f->data)

在宏中使用了三元操作符 "? :", 但前后的值一样, 并且判断条件是常量 0, 这是怎么回事?
难道作者闲得无聊才这么做么? 问题可不这么简单吧...

 

问题思考和解决:

再参考一下另一个宏, 也许对判断作者的意图有所启发:

#define SSIZE(str)   (str->size + 0)

在这个宏中,  size 是一个整形字段, 所以加上 0 并不会改变最后的 size 值, 不过这个表达式
使得 SSIZE(str) 成为右值 (rvalue) 表达式了, 所以其可以读取, 但不能写入了:

   int len = SSIZE(str);    // 合法, SSIZE(str) 是作为右值
   SSIZE(str) = len;         // 编译错误, SSIZE(str) 即 (str->size + 0) 不能作为左值.

这样, 我们猜测将 XFLOAT_DATA() 宏写成那个样子, 也是有着同样的意图的.
但没有使用 (f->data + 0) 的却另有原因了. 因为编译器对于整形运算可以施加各种优化,
如 size+0 可直接优化为 size, 但是对于浮点数 f+0 却不一定做这样的优化.
如果宏写为 (f->data + 0) 会带来额外的运行时开销, 这种开销是难以接受的, 对于处处
考虑性能的 C/C++ 程序猿而言.

所以, 写成了 (0 ? f->data : f->data) 这种奇怪的表达式方式了.

可是事情还没完, 老编译器不能将 "?:" 表达式作为左值使用, 但是新编译器却可以:

   int x = 1, y, z;
   (x ? y : z) = 3;    // 竟然合法, 结果是 y = 3.
   XFLOAT_DATA(f) = 3.14;   // 竟然合法! 作者意图未达到...

 

所以, 辛苦写为 (0 ? f->data : f->data) 无法达到阻止左值的意图, 如果作者真的唯一意图如此的话.

 

最后, 考虑的一种解决方式还是用我们的老朋友 const 关键字:

#define XFLOAT_DATA(f)   ((const double) (f->data)) 

这下表达式 "XFLOAT_DATA(f) = 3.14" 编译器终于可以阻止其编译了.
    error C2106: “=”: 左操作数必须为左值.

可能的错误也就可以避免了. 也许也就拯救了地球说不定...

 

你可能感兴趣的:(C++)