什么是双重评价?为什么要避免? 在C++中,我使用了像这样的宏 #define max(a,b) (a > b ? a : b)

什么是双重评价?为什么要避免? 在C++中,我使用了像这样的宏 #define max(a,b) (a > b ? a : b),c++,macros,preprocessor-directive,C++,Macros,Preprocessor Directive,可能导致“双重评估”。有人能给我举个例子,说明什么时候会发生双重评估,以及为什么它不好吗 注:令人惊讶的是,我在谷歌搜索时找不到任何详细的解释,除了中的一个例子(我不理解)。想象一下你写了这样一个: #define Max(a,b) (a < b ? b : a) int x(){ turnLeft(); return 0; } int y(){ turnRight(); return 1; } 您知道turnRight()将执行两次吗?该宏,Max将扩展为: auto var

可能导致“双重评估”。有人能给我举个例子,说明什么时候会发生双重评估,以及为什么它不好吗


注:令人惊讶的是,我在谷歌搜索时找不到任何详细的解释,除了中的一个例子(我不理解)。

想象一下你写了这样一个:

#define Max(a,b) (a < b ? b : a)

int x(){ turnLeft();   return 0; }
int y(){ turnRight();  return 1; }
您知道
turnRight()
将执行两次吗?该宏,
Max
将扩展为:

auto var = (x() < y() ? y() : x());
自动变量=(x() 在评估条件
x()
之后,程序将在
y():x()
:在本例中为
true
,它将第二次调用
y()
。看到了

简单地说,将一个作为参数传递给您的,
Max
可能会对该表达式求值两次,因为该表达式将在宏定义中使用它所具有的宏参数时重复。请记住,宏由处理


所以,底线是,不要仅仅因为希望函数是泛型的而使用宏来定义函数(在本例中实际上是表达式),而可以使用函数模板来有效地定义函数


P:C++具有模板函数。在宏定义中,出现了两个次:

<代码> A<代码> >代码> B/COD>两次。因此,如果将它与具有副作用的参数一起使用,副作用将执行两次

max(++i, 4);

如果调用前i=4,则返回6。由于这不是预期的行为,您应该更喜欢使用内联函数来替换诸如
max
之类的宏

考虑以下表达式:

 x = max(Foo(), Bar());
其中
Foo
Bar
如下所示:

auto var = Max(x(), y());
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 <代码>示例中,它不返回相同的值。

< P>在C和C++中的宏语言在一个预处理阶段由一个专用的解析器处理;标记被转换,然后输出被输入到解析器的输入流中<代码> >定义< /C> >和<代码>包含令牌不能被C或C++解析器自己识别。

这一点很重要,因为这意味着当宏被称为“扩展”时,它的字面意思是。给定

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

int f(){std::cout I可能会这样解释:如果a和b是复杂的或函数调用,它们将被评估不止一次。对性能不好,甚至可能产生错误。如果
a
b
是有副作用的函数调用呢?如果
a
b
x++/code>或
+x
呢>?还与宏参数中的副作用有关:@BenVoigt好的,我现在明白它们的意思了:如果
a
((抛出0),0)
,那么肯定
a
只被评估一次,而
b
被评估一次或零次。你是对的,两次评估都不可能。考虑:
pips=max(rand(),5)+1;//Ord/<代码>。这是保证在[1,6]中返回一个整数吗?如果<代码> max()>代码>是一个涉及双重评估的宏。这个例子的分辨率是什么?在C++标准库中有一个最大值吗?取决于C++标准版本(例如C++ 14)。@ PeterMortensen一直有一个模板函数在C++中,尽管它已经被修改来反映最近C++的变化version@PatrickM邦戈:这两点似乎都毫无意义。
std::max(12.f,13.)是消除歧义的正常方法。没有任何函数可以延长它的参数的生存期,这不是C++的工作原理。它并不重要:引用不在整个表达式结束时一直纠缠。只有用它初始化另一个引用才是重要的,在这点上你显然是负责的。您的引用。@ MalSalter不,在理智的情况下,您返回为“代码>普通类型Type < /Calp>,因为这是免费的,<代码>:<代码>。Calg中的注释支持双评价问题。在C++中,我们还有其他的技术可用。这不是一个未受影响的行为<代码>((++i)<(4)?(++i)(4))
,因为首先计算
++i
后会有一个增量。但是,在同一行的同一变量上使用多个增量通常不是一个好主意。