Ответ 1
Причина в том, что у компилятора есть много дел, не будучи полноправным интерпретатором, способным оценивать произвольный код на С++.
Если они придерживаются единичных выражений, они ограничивают число случаев, которые нужно рассматривать резко. Как бы то ни было, это упрощает многое, что нет точек с запятой в частности.
Каждый раз, когда встречается a ;
, это означает, что компилятор должен иметь дело с побочными эффектами. Это означает, что в предыдущем утверждении было изменено некоторое локальное состояние, на которое полагается следующее утверждение. Это означает, что оцениваемый код - это не просто серия простых операций, каждый из которых принимает в качестве входных данных предыдущий выходной сигнал, но также требует доступа к памяти, о чем гораздо труднее рассуждать.
В двух словах:
7 * 2 + 4 * 3
просто вычислить. Вы можете построить дерево синтаксиса, которое выглядит так:
+
/\
/ \
* *
/\ /\
7 2 4 3
и компилятор может просто пересечь это дерево, выполняя эти примитивные операции на каждом node, а корень node неявно является возвращаемым значением выражения.
Если бы мы должны были написать одно и то же вычисление с использованием нескольких строк, мы могли бы сделать это следующим образом:
int i0 = 7;
int i1 = 2;
int i2 = 4;
int i3 = 3;
int i4 = i0 * i1;
int i5 = i2 * i3;
int i6 = i4 + i5;
return i6;
что гораздо труднее интерпретировать. Нам нужно обрабатывать чтение и запись в памяти, и мы должны обрабатывать операторы return. Наше синтаксическое дерево стало намного сложнее. Нам нужно обрабатывать объявления переменных. Нам нужно обрабатывать операторы, которые не имеют возвращаемого значения (например, цикл или запись в память), но которые просто меняют какую-то память. Какая память? Где? Что делать, если он случайно перезаписывает часть собственной памяти компилятора? Что делать, если это segfaults?
Даже без всяких противных "what-if" код, который должен интерпретировать компилятор, стал намного сложнее. Дерево синтаксиса теперь может выглядеть примерно так: (LD
и ST
- операции загрузки и хранения соответственно)
;
/\
ST \
/\ \
i0 3 \
;
/\
ST \
/\ \
i1 4 \
;
/\
ST \
/ \ \
i2 2 \
;
/\
ST \
/\ \
i3 7 \
;
/\
ST \
/\ \
i4 * \
/\ \
LD LD \
| | \
i0 i1 \
;
/\
ST \
/\ \
i5 * \
/\ \
LD LD \
| | \
i2 i3 \
;
/\
ST \
/\ \
i6 + \
/\ \
LD LD \
| | \
i4 i5 \
LD
|
i6
Он не только выглядит намного сложнее, но и теперь требует состояния. Раньше каждое поддерево можно было интерпретировать изолированно. Теперь все они зависят от остальной части программы. Одна из операций листа LD не имеет смысла, если она не помещается в дерево, так что операция ST
была выполнена ранее в этом же месте.