什么是双重评估,为什么要避免?

我正在用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模板函数。

ab在macros定义中出现两次。 所以如果你使用带有副作用的参数,副作用会被执行两次。

 max(++i, 4); 

如果在呼叫之前i = 4 ,将返回6。 由于这不是预期的行为,所以您应该更喜欢内联函数来replace像max这样的macros。

考虑下面的expression式:

  x = max(Foo(), Bar()); 

FooBar是这样的:

 int Foo() { // do some complicated code that takes a long time return result; } int Bar() { global_var++; return global_var; } 

然后在原来的maxexpression式扩展如下:

  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函数,优化器可能无法避免两次调用该函数 。