Мне нравится рефакторинг сложных условий, таких как это:
if (foo(blah) || (bar(param1, param2, param3) && !obj.longMethodName())) ...
в это:
bool foo_true = foo(blah);
bool bar_true = bar(param1, param2, param3);
bool long_true = obj.longMethodName();
if (foo_true || (bar_true && !long_true)) ...
Я думаю, что это делает код намного проще для понимания, и это помогает с отладкой, потому что вы можете видеть промежуточные результаты, используемые для вычисления конечного условия.
Но: в исходном коде из-за короткого замыкания, bar
будет оцениваться только если foo
возвращается true
, а также longMethodName
только если bar
возвращается true
,
Предположим, что функции «чистые», методы const
и компилятор может видеть тела функций всего. Разрешено ли оптимизаторам C ++ откладывать оценку моих промежуточных значений до тех пор, пока они не понадобятся?
Конечно. При условии, что компилятор может видеть достаточно, чтобы определить
тот foo
, bar
а также obj.longMethodName()
не имеют
другое влияние на наблюдаемое поведение вашего кода.
Есть ли какие-либо компиляторы это другой вопрос. Я скорее сомневаюсь
Это; это потребовало бы особой логики, которой нет в
обычный список методов оптимизации, для чего-то, что
практически никто не делает. (Я подозреваю, что большинство программистов
найти оригинальную версию, правильно отформатированную, чтобы быть более
удобочитаемее, чем тот, с большим количеством дополнительных переменных.)
Интересно, стоит ли указывать, что компилятор разрешен
вызвать все три функции, даже если написано if:
if ( foo( blah ) || (bar( p1, p2, p3 ) && ! obj.lMN() ) )
(Хотя я не могу себе представить, что бы.) Стандарт делает
нет требований в отношении того, какие функции вызываются, когда;
это только требует, чтобы наблюдаемое поведение было таким же
значения и в том же порядке — никаких гарантий в отношении
время) «как будто» формальная семантика программы была соблюдена.
И единственное, что считается наблюдаемым поведением, это IO
(в некоторой форме) и доступ к изменчивым объектам.
Нет. Ваш компилятор не имеет права проводить оптимизацию, потому что он не может определить, имели ли вы в виду короткое замыкание или хотите ли вы иметь потенциальный побочный эффект оценки bar
не важно что.
Нет. C ++ не имеет понятия чистого метода без побочных эффектов, поэтому на самом деле нет способа его оптимизировать.
Проблема здесь в том, что foo
а также bar
может быть реализован в другом модуле компиляции, и C ++ не имеет понятия чистоты функции. Это означает, что foo
а также bar
может иметь побочные эффекты (изменения в экране или глобальные переменные) и, следовательно, должен быть оценен, чтобы получить ожидаемое поведение.
Интересно, что с помощью GCC функции могут быть объявлены с помощью pure
приписывать. Это говорит компилятору, что функция не имеет побочных эффектов. И потому можно назвать ленивым. Увидеть Вот Больше подробностей.
Я не уверен, что назначение будет считаться побочным эффектом уже. По меньшей мере, трудно определить, безопасно ли перемещать реальный вызов.
Но я хотел бы отметить, что в C ++ 11 можно добиться того, что OP выполняет с почти таким же синтаксисом, который OP использует в примерах, использующих std::bind
.
Это просто foo_true
, не будет определяться как
bool foo_true = foo(blah);
скорее
auto foo_true = std::bind(foo, blah)
,
if
может быть затем проверен как if( foo_true() || bar_true() )
,
Будь то чище или нет, это личное дело ИМО. Но я считаю, что он ведет себя так, как хотел, так и ожидал. Полный код:
#include <iostream>
#include <functional>
using namespace std;
bool foo(int blah){
cout << "blah: " << blah << '\n';
return blah;
}
bool bar(bool negate_me){
cout << "negate_me: " << negate_me << '\n';
return !negate_me;
}
int main() {
bool test = true;
int param = 42;
auto foo_true = std::bind(foo, test);
auto bar_true = std::bind(bar, param);
if (foo_true() || bar_true() ) cout << "TEST\n";
return 0;
}
Выход:
blah: 1
TEST
bar
не звонил. + Изменить test
в false
и так будет.