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

时间:2016-09-11 18:13:35

标签: c++ macros preprocessor-directive

我正在使用像

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

可以导致'双重评估'。有人能给我一个例子,说明何时进行双重评估以及为什么这样做不好?

P.S。:令人惊讶的是,除了Clojure中的一个例子(我无法理解)之外,谷歌搜索时找不到任何详细的解释。

4 个答案:

答案 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 macroMax可能会对该表达式进行两次计算,因为表达式将在其所采用的宏参数处重复,用于宏的定义。请记住,宏由preprocessor 处理。

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

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

答案 1 :(得分:23)

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

max(++i, 4);
如果i = 4在通话前

将返回6。由于它不是预期的行为,您应该更喜欢内联函数来替换max之类的宏。

答案 2 :(得分:20)

考虑以下表达式:

 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示例中,它不会一致地返回相同的值。

答案 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());
}

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

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