Что такое двойная оценка и почему ее следует избегать? [дубликат]

этот вопрос уже есть ответ здесь:

Я читал это на C++, используя макросы, такие как

#define max(a,b) (a > b ? a : b)

может привести к двойной оценки. Может ли кто-нибудь дать мне пример того, когда происходит двойная оценка и почему это плохо?

P.S.: удивительно, что я не мог найти подробного объяснения, когда гуглил для него, за исключением примера в Clojure (который я не могу понять).

4 ответов


представьте, что вы написали это:

#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() на второй раз. Смотри!--20-->Живу На Coliru.

проще говоря, передав выражение в качестве аргумента в свой макрос, Max потенциально будет оценивать это выражение дважды, потому что выражение будет повторяться там, где когда-либо параметр макроса, который он принимает, используется в определении макроса. Помни,макросы обрабатываются препроцессора.


Итак, суть в том, что не используйте макросы для определения функция (фактически выражение в этом случае) просто потому, что вы хотите, чтобы она была generic, в то время как это можно эффективно сделать с помощью шаблонов функций

PS: C++ имеет std::max функции шаблона.


a и b происходить два раза в определении макроса. Поэтому, если вы используете его с аргументами, имеющими побочные эффекты, Побочные эффекты выполняются два раза.

max(++i, 4);

вернет 6, если i = 4 перед вызовом. Поскольку это не ожидаемое поведение, вы должны предпочесть встроенные функции для замены таких макросов, как max.


рассмотрим следующее выражение:

 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 пример, он не возвращает одно и то же значение последовательно.


язык макросов в C и c++ обрабатывается выделенным синтаксическим анализатором на этапе "предварительной обработки"; токены переводятся и вывод затем подается во входной поток собственно синтаксического анализатора. #define и #include фишки не распознается самими парсерами 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());
}

live demo:http://ideone.com/3JBAmF

аналогично, если бы мы вызывали extern функция, оптимизатор не может быть в состоянии избегайте вызова функции дважды.