C / C ++中不缺少类似功能的坏或宏的例子。
#define SQUARE(x) x * x
printf("%d", SQUARE(4)); //16
printf("%d", SQUARE(3+1)); //7
#undef SQUARE
#define SQUARE(x) (x) * (x)
printf("%d", 36/4); //9
printf("%d", SQUARE(6)/SQUARE(2)); //36
#undef SQUARE
#define SQUARE(x) ((x) * (x))
int x = 3;
++x;
printf("%d", SQUARE(x)); //16
int y = 3;
printf("%d", SQUARE(++y)); //?
#undef SQUARE
考虑到类似函数的宏的问题,它们有什么样的好/谨慎/推荐用途?
是否有任何时候类似函数的宏更适合某个函数?
我认为必须有一些非常好的案例,否则预处理器制造商会让他们的工作变得更容易并将其排除在外。
答案 0 :(得分:3)
评论已经抓住了大部分内容。
某些类型的调试和检测只能通过使用函数式宏来完成。最好的示例是assert()
,但函数式宏也可以用于检测代码以进行性能分析。像__FILE__
和__LINE__
这样的魔术宏以及用于引用的#
和用于令牌粘贴的##
等功能会使类似函数的宏对调试和分析有价值。
在C ++中,使用模板和通常更具侵略性的内联,使用函数式宏的其他原因很少(如果有的话)。例如,出于问题中说明的原因,模板函数std::max
是一个比MAX
宏更好的解决方案。
在C中,有时需要优化以确保内联一小段代码。在这种情况下,宏仍然偶尔有用。 ALLCAPS命名约定用于警告程序员,由于简单文本替换的所有问题,这实际上是一个宏而不是函数。例如,如果您在一个性能关键的代码段中有几个需要等效std::max
的地方,那么宏 - 具有所有危险 - 可能是一个有用的解决方案。
答案 1 :(得分:2)
大多数情况下您不需要使用宏。但是有些情况是合法的(尽管有讨论的余地)。
如果可以使用枚举,则不应使用宏。您不应该有依赖于具有魔术名称的局部变量的宏。您不应该有可以用作l值的宏。您不应该有对扩展宏周围的代码有副作用的宏。使用宏代替内联函数大多数时候都是一个坏主意,无论如何列表都是无穷无尽的。
你可以使用宏来伪造迭代器,但在C语言中,尤其是Linux内核,你会看到以下内容:
#define list_for_each_safe(pos, n, head) \
for (pos = (head)->next, n = pos->next; pos != (head); \
pos = n, n = pos->next)
在整个Linux内核源代码中使用了许多其他类似类型的宏。
offsetof(3)通常也实现为宏,以及assert(3)等。
答案 2 :(得分:1)
我在这里普遍同意dmp。如果您可以使用其他东西,则使用它并忘记宏。
那说我会重复评论中的内容:
我重复一遍 - 如果你能避免预处理器就这样做。
答案 3 :(得分:0)
C标准有(或允许)一大堆它们,我认为任何以相同的精神做宏的事情应该是合法的:
isspace
和类似函数通常以宏的形式实现CMPLX
被声明为宏UINT64_C
和类似的宏tgmath.h
,sin
,cos
并且许多其他功能都会成为宏_Generic
关键字的重点是编写类似函数的宏答案 4 :(得分:0)
在某些情况下,会有编译器特定的指令,这些指令会因每个编译器的重写而变化并且噪声很大。我经常使用的一个属于此类的宏用于通知编译器参数未使用:
virtual size_t heightForItemAtIndex(const size_t& idx) const {
MONUnusedParameter(idx); // << MONUnusedParameter() is the macro
return 12; // << this type's items are all the same height
}
此处更好地介绍了问题:cross platform macro for silencing unused variables warning
答案 5 :(得分:0)
Google Style C++ Guide有一个非常有用的宏的例子(我不确定这是否是'函数式宏'的意思 - 它需要一个参数但它不能被函数调用替换):
DISALLOW_COPY_AND_ASSIGN(类名);
这样的宏可以放在类的私有部分中,以禁用由编译器自动生成的复制构造函数和赋值运算符。除非一个类确实需要它们,否则通常不允许这两个自动生成的方法。没有宏禁用需要在每个类中进行大量输入:
ClassName(const ClassName&amp;);
void operator =(const ClassName&amp;);
宏只是自动生成此代码。这很安全。我不知道这种宏导致问题的任何情况。