有没有什么窍门禁止C宏被称为左值?

问题描述:

例如,有没有什么窍门禁止C宏被称为左值?

struct node { 
    struct node *left, *right; 
}; 
#define LEFT(X) (X->left) 
#define RIGHT(X) (X->right) 

我想禁止这样的宏调用不改变现有的宏接口。

LEFT(n) = ... 

任何想法?

+0

该宏是做什么的?我不明白你为什么需要这样的宏。 – Lundin 2011-04-15 11:48:02

+0

@Lundin我以此为例。对于真正的代码,有很多深层次的成员,accessor-macros会很有用。 – wsxiaoys 2011-04-15 11:52:22

+0

@wsxiaoys好的,我明白了。我会用一个例子说明你应该如何做这样的宏。 – Lundin 2011-04-15 11:58:16

试试这个:

#define LEFT(X) ((X)->left+0) 
#define RIGHT(X) ((X)->right+0) 
+0

这看起来像最简单的一个,但它可能不像三元运算符那样优化我猜。 – wsxiaoys 2011-04-15 11:36:29

+0

哇! ......简单而efffective – BiGYaN 2011-04-15 11:37:22

+0

@wsxiaoys:难道你真的东西,恒定加'0'是比较困难的修剪分支从来没有?如果有的话肯定更简单! – 2011-04-15 11:45:20

#undef LEFT 
#undef RIGHT 

//template<class T> 
inline const node * const LEFT(const node * X) { 
    return X->left; 
} 
+0

这里的T是什么? – sharptooth 2011-04-15 11:16:29

+0

有点smarmy,但+1。最好的建议是根本不使用宏。 – 2011-04-15 11:16:43

+0

用C++你没有宏... – Petesh 2011-04-15 11:17:09

我不认为有什么办法来防止这种情况。可能最好的办法是不使用宏。

+1

这是解决问题的恰当方法。 – Lundin 2011-04-15 11:45:45

也许const,但它需要一个明确的类型:

#define LEFT(X) ((const struct node *) (X->left)) 

...但如果你有typeof编译器扩展:

#define LEFT(X) ((const typeof(X->left)) (X->left)) 
+1

这些仍然是左值。 OP正试图使它不可能编辑指针,而不是它指向的数据。 – 2011-04-15 11:28:58

我会用内联函数去,但如果你想要一个宏:

#define LEFT(X) (1 ? (X)->left : 0) 

你可以使用三元运算符来CE宏的结果是右值,但错误信息可能会造成混淆用户:

struct node { 
    node *left, *right; 
}; 
#define LEFT(x) (true ? (x).left : (node*)0) 
int main() { 
    node n; 
    // LEFT(n); // error 
    node *l = LEFT(n); // OK 
} 

权谋有在特定运营商的语义。只包含操作符的表达式的类型是表达式两个分支的常见类型(或可从中转换而来的类型),即使只评估其中一个分支。现在,当编译器评估true ? x.left : (node*)0时,它解析为表达式x.left,但具有常见类型x.left(node*)0。他们两个基本上是相同的类型,唯一的细节(node*)0是一个右值,而不是一个左值。

+0

或者直接写'node * l = n.left;'......问题解决了。 – Lundin 2011-04-15 11:50:41

我刚刚去了一个明确的转换

#define LEFT(X) ((struct node*)(X->left)) 
#define RIGHT(X) ((struct node*)(X->right)) 

这样铸造的结果始终是一个右值。如果除了你不想让改变什么指针指向添加const作为另一个答案已经给出:

#define LEFT(X) ((struct node const*)(X->left)) 
#define RIGHT(X) ((struct node const*)(X->right)) 

你不写这样的“访问”宏,你做

typedef struct 
{ 
    ... 
} X; 

X x; 

#define LEFT (x.y.z.left) 

然后,像访问其他变量一样访问LEFT宏。这使得代码更具可读性,同时有效地禁用与功能宏相关的整个不安全,丑陋,容易出错,未定义行为的芭蕾。

对于C++,我会用一元+:

#define LEFT(X) (+((X)->left)) 
#define RIGHT(X) (+((X)->right)) 

对于一般的宏观情况下,这个拥有添加+0,它也适用于void*和函数指针的优势。对于C,您可以使用逗号运算符

#define LEFT(X) (0, ((X)->left)) 
#define RIGHT(X) (0, ((X)->right)) 
+0

一般来说,这比我的回答好,因为它也适用于'void *'。对于OP的问题,这并不重要。 – 2011-04-15 12:23:36

+0

@R ..,你是对的。更新我的答案更精确:) – 2011-04-15 12:26:18

+0

最后我加入'+ 0'在我的代码,因为使用逗号操作这种方式产生了大量的“表达式结果未使用”警告 – wsxiaoys 2011-04-15 12:33:04