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

What is double evaluation and why should it be avoided?

我在 C++ 中使用像

这样的宏阅读
#define max(a,b) (a > b ? a : b)

可能导致 'double evaluation'。谁能给我一个双重评估何时发生以及为什么不好的例子?

P.S.: 令人惊讶的是,除了 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()会被执行两次吗?该宏 Max 将扩展为:

auto var = (x() < y() ? y() : x());

评估条件 x() < y() 后,程序会在 y() : x() 之间进行所需的分支:在我们的例子中 true,它为 [=38] 调用 y() =]第二次。看到了吧Live On Coliru.

简单的说,传递一个expression as an argument to your function-like macro, Max will potentially evaluate that expression twice, because the expression will be repeated where ever the macro parameter it takes on, is used in the macro's definition. Remember, macros are handled by the preprocessor.


所以,最重要的是,不要仅仅因为你希望它是 generic 而使用宏来定义一个函数(在这种情况下实际上是一个表达式),而它可以是使用函数模板有效地完成

PS: C++ 有一个 std::max 模板函数。

ab 在宏定义中出现了两次。因此,如果您将它与具有副作用的参数一起使用,则副作用会执行两次。

max(++i, 4);

将 return 6 if i = 4 调用前。由于这不是预期的行为,您应该更喜欢内联函数来替换 max 等宏。

考虑以下表达式:

 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;
}

然后在原来的 max 表达式中扩展为:

 Foo() > Bar() ? Foo() : Bar();

无论哪种情况,Foo 或 Bar 都会执行两次。从而花费比必要更长的时间或改变程序状态的次数超过预期的次数。在我的简单 Bar 示例中,它不会始终 return 相同的值。

C和C++中的宏语言在'pre-processing'阶段由专用解析器处理;标记被翻译,然后输出被送入适当的解析器的输入流。 #define#include 标记 不是 C 或 C++ 解析器本身识别的。

这很重要,因为这意味着当一个宏被称为 "expanded" 时,它的意思是 字面意思 。鉴于

#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());

如果compiler/optimizer可以证明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());
}

现场演示:http://ideone.com/3JBAmF

同样,如果我们调用 extern 函数,优化器可能无法 avoid calling the function twice