Сравнительно легко доказать эквивалентность для двух упрощенных случаев трех подвыражений:
a && (b && c) --> a && bc // bc is a shorthand for b && c
Здесь a
будет оцениваться первым. Если оно ложно, короткое замыкание помешает оценке bc
. Если это правда, bc
будет оцениваться, то есть b && c
будет оцениваться. Если b
равно false, c
не будет оцениваться.
(a && b) && c --> ab && c // ab is a shorthand for a && b
Здесь ab
будет оцениваться первым. (То есть a && b
оценивается первым. Если a
ложно, короткое замыкание помешает оценке b
. В противном случае ab
приводит к b
.) Если ab
ложно, c
выиграно не будет оцениваться.
Теперь, если вы предпочитаете доказательство доказательству, вы можете посмотреть на вывод сборки следующего кода C:
int a(), b(), c(), d();
void e()
{
a() && b() && c() && d();
}
void f()
{
a() && (b() && (c() && d()));
}
void g()
{
(a() && b()) && (c() && d());
}
void h()
{
((a() && b()) && c()) && d();
}
(я использовал код C вместо кода C ++ для предотвращения искажения имени.)
созданная сборка для e
:
_e:
// ... enter ...
call _a
testl %eax, %eax
je L1
call _b
testl %eax, %eax
je L1
call _c
testl %eax, %eax
je L1
call _d
testl %eax, %eax
nop
L1:
// ... leave ...
созданная сборка для f
:
_f:
// ... enter ...
call _a
testl %eax, %eax
je L4
call _b
testl %eax, %eax
je L4
call _c
testl %eax, %eax
je L4
call _d
testl %eax, %eax
nop
L4:
// ... leave ...
созданная сборка для g
:
_g:
// ... enter ...
call _a
testl %eax, %eax
je L7
call _b
testl %eax, %eax
je L7
call _c
testl %eax, %eax
je L7
call _d
testl %eax, %eax
nop
L7:
// ... leave ...
созданная сборка для h
:
_h:
// ... enter ...
call _a
testl %eax, %eax
je L10
call _b
testl %eax, %eax
je L10
call _c
testl %eax, %eax
je L10
call _d
testl %eax, %eax
nop
L10:
// ... leave ...
Как видите, кроме меток, сгенерированный код сборки полностью идентичен.