我看到有人在使用C++的宏
#define max(a,b) (a > b ? a : b)
“double evaluation”可能导致问题。有人能举个例子说明何时会出现双重评估,以及为什么它是不好的吗?
P.S.:令人惊讶的是,在谷歌搜索中我找不到任何详细的解释,除了在Clojure中的一个例子(我无法理解)。
我看到有人在使用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
,这会导致对 y()
进行第二次调用。请参见 Live On Coliru。Max
可能会导致该表达式被评估两次,因为该表达式将在宏参数所采用的所有位置上重复使用,在宏定义中使用。记住,宏由预处理器处理。
所以,底线是,不要使用宏定义函数(实际上在这种情况下是表达式),仅仅因为你想让它变得通用,而可以通过使用函数模板有效地实现。
附注:C++有一个std::max
模板函数。
std::max<double>(12.f,13.)
是消除歧义的正常方式。而且没有任何函数可以延长其参数的生命周期,这不是 C++ 的工作方式。虽然这并不重要:引用直到完整表达式的末尾才会悬空。只有在使用它初始化另一个引用时才会产生影响,那时你显然要对你的引用负责。 - MSalterscommon_type_t<Ts...>
,因为这可以使用?:
免费实现。 - Griwesa
和b
在宏定义中出现了两次。因此,如果您将其与具有副作用的参数一起使用,则副作用会执行两次。
max(++i, 4);
如果在调用之前i = 4
,将会返回6。因为这不是期望的行为,所以你应该优先使用内联函数来替换这样的宏,比如max
。
((++i) < (4) ? (++i) (4))
,因为在评估第一个 ++i
后存在 序列点。然而,在同一行上对同一变量进行多次增量操作可能通常不是一个好主意。 - Daerdemandt 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
示例中,它不能始终返回相同的值。
#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;
#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
函数,优化器可能无法避免调用函数两次。
a
或b
是带有副作用的函数调用会怎样?如果a
或b
是x++
或++x
会怎样? - James Mpips = max(rand(), 5) + 1; // 投掷骰子
。这能保证返回一个在[1,6]之间的整数吗?如果max()
是涉及双重求值的宏定义,则不能保证。 - njuffamax(i, a.pop_and_ret())
或max(x++, y++)
,在预处理期间会扩展为(x++ > y++ ? x++ : y++)
,因此优化器不知道有宏扩展。 - kfsone#define max(a,b) ((a) > (b) ? (a) : (b))
- CodesInChaos