Что такое двойная оценка и почему ее следует избегать? [дубликат]
этот вопрос уже есть ответ здесь:
Я читал это на 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
функция, оптимизатор не может быть в состоянии избегайте вызова функции дважды.