我正在使用像
这样的宏在C ++中阅读#define max(a,b) (a > b ? a : b)
可以导致'双重评估'。有人能给我一个例子,说明何时进行双重评估以及为什么这样做不好?
P.S。:令人惊讶的是,除了Clojure中的一个例子(我无法理解)之外,谷歌搜索时找不到任何详细的解释。
答案 0 :(得分:66)
想象一下你写了这个:
#define Max(a,b) (a < b ? b : a)
int x(){ turnLeft(); return 0; }
int y(){ turnRight(); return 1; }
然后这样叫:
auto var = Max(x(), y());
你知道turnRight()
会被执行两次吗?该宏Max
将扩展为:
auto var = (x() < y() ? y() : x());
在评估条件x() < y()
之后,程序会在y() : x()
之间获取所需的分支:在我们的案例中true
,其中第二次调用y()
。见Live On Coliru。
简单地说,将expression作为参数传递给function-like macro,Max
可能会对该表达式进行两次计算,因为表达式将在其所采用的宏参数处重复,用于宏的定义。请记住,宏由preprocessor 处理。
所以,底线是,不要使用宏来定义一个函数(在这种情况下实际上是一个表达式),因为你希望它是泛型,而它可以使用函数有效地完成模板
PS:C ++有一个std::max
模板函数。
答案 1 :(得分:23)
a
和b
在宏定义中出现两次。因此,如果您将其与具有副作用的参数一起使用,则副作用将被执行两次。
max(++i, 4);
如果i = 4
在通话前将返回6。由于它不是预期的行为,您应该更喜欢内联函数来替换max
之类的宏。
答案 2 :(得分:20)
考虑以下表达式:
x = max(Foo(), Bar());
Foo
和Bar
的位置如下:
int Foo()
{
// do some complicated code that takes a long time
return result;
}
int Bar()
{
global_var++;
return global_var;
}
然后在原始的max
表达式中扩展如下:
Foo() > Bar() ? Foo() : Bar();
在任何一种情况下,Foo或Bar都要执行两次。从而花费超过必要的时间或改变程序状态超过预期的次数。在我的简单Bar
示例中,它不会一致地返回相同的值。
答案 3 :(得分:7)
C和C ++中的宏语言由预处理&#39;中的专用解析器处理。阶段;翻译令牌,然后将输出馈送到解析器本身的输入流中。 C语言或C ++解析器本身无法识别#define
和#include
令牌 。
这很重要,因为这意味着当一个宏被称为&#34;扩展&#34;它意味着字面意思。给定
#define MAX(A, B) (A > B ? A : B)
int i = 1, j = 2;
MAX(i, j);
C ++解析器看到的是
(i > j ? i : j);
但是,如果我们将宏用于更复杂的东西,则会发生相同的扩展:
MAX(i++, ++j);
扩展为
(i++ > ++j ? i++ : ++j);
如果我们传递了一个函数调用的东西:
MAX(f(), g());
这将扩展到
(f() > g() ? f() : g());
如果编译器/优化器可以证明f()
没有副作用,那么它会将其视为
auto fret = f();
auto gret = g();
(fret > gret) ? fret : gret;
如果它不能,则必须两次调用f()和g(),例如:
#include <iostream>
int f() { std::cout << "f()\n"; return 1; }
int g() { std::cout << "g()\n"; return 2; }
#define MAX(A, B) (A > B ? A : B)
int main() {
MAX(f(), g());
}
同样,如果我们调用extern
函数,优化程序可能无法avoid calling the function twice。