什么是双重评估,为什么要避免?
我正在用C ++读取这个macros
#define max(a,b) (a > b ? a : b)
可以导致“双重评估”。 有人能给我举一个例子,说明什么时候发生双重评估,为什么不好?
PS:奇怪的是我找不到任何详细的解释,除了在Clojure (我不明白)的例子。
想象一下,你写这个:
#define Max(a,b) (a < b ? b : a) int x(){ turnLeft(); return 0; } int y(){ turnRight(); return 1; }
然后这样调用它:
auto var = Max(x(), y());
你知道turnRight()
会执行两次吗? 那个macros, Max
会扩展到:
auto var = (x() < y() ? y() : x());
在评估了条件x() < y()
,程序接着在y() : x()
之间取得所需的分支:在我们的例子中是true
, 第二次调用y()
。 看到它住在Coliru 。
简单地说,将一个expression式作为parameter passing给类似函数的macros , Max
将潜在地评估该expression式两次,因为在macros的定义中使用macrosexpression式时,expression式将被重复。 请记住, macros是由预处理器处理的 。
所以,底线是,不要使用macros来定义一个函数(在这种情况下,实际上是一个expression式),因为您希望它是通用的 ,而使用函数模板
PS:C ++有一个std::max
模板函数。
a
和b
在macros定义中出现两次。 所以如果你使用带有副作用的参数,副作用会被执行两次。
max(++i, 4);
如果在呼叫之前i = 4
,将返回6。 由于这不是预期的行为,所以您应该更喜欢内联函数来replace像max
这样的macros。
考虑下面的expression式:
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
expression式扩展如下:
Foo() > Bar() ? Foo() : Bar();
无论哪种情况,Foo或Bar都要执行两次。 从而花费比所需时间更长的时间或改变程序状态超过预期的次数。 在我简单的Bar
示例中,它不会始终返回相同的值。
C和C ++中的macros语言在“预处理”阶段由专用parsing器处理; 令牌被翻译并且输出然后被馈送到parsing器本身的inputstream中。 #define
和#include
标记不被C或C ++parsing器自己识别。
这很重要,因为这意味着当一个macros被说成是“扩展”的时候,这就意味着这个意思 。 特定
#define MAX(A, B) (A > B ? A : B) int i = 1, j = 2; MAX(i, j);
C ++parsing器看到的是什么
(i > j ? i : j);
但是,如果我们用更复杂的macros来使用macros,就会发生相同的扩展:
MAX(i++, ++j);
扩大到
(i++ > ++j ? i++ : ++j);
如果我们传递一个可以调用函数的东西:
MAX(f(), g());
这将扩大到
(f() > g() ? f() : g());
如果编译器/优化器可以certificatef()
没有副作用,那么它将把它当作
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()); }
现场演示: http : //ideone.com/3JBAmF
同样,如果我们调用一个extern
函数,优化器可能无法避免两次调用该函数 。