Сегодня тривиальный пост.
Как распарсится это выражение?
a+++++b;Варианты:
-
((a++) ++) + b;-
a + (++(++b));-
(a++) + (++b);Последнее можно отмести сразу, т.к. очень плохо обобщается на более сложные случаи (как вообще такое разбирать?). Давайте выберем из двух других.
В C++ разбор идёт примерно слева направо сверху вниз, причём в текущий токен парсер пытается набрать как можно больше символов (это называется maximum munch, т.е. такая жадная стратегия), потому тут конечно можно распарсить как
a + ++++b, но можно добрать ещё один плюс, чтобы получился не бинарный оператор плюс, а постфиксный инкремент, потому верен первый вариант. Ну и тут же легко понять, почему это не скомпилируется: a++ это prvalue (значение оригинального идентификатора a изменяется, но возвращается копия), когда как инкремент/декремент требуют lvalue в качестве аргумента.Обратите внимание, что если тут расставить пробелы, то всё будет работать как надо (потому что пробел заканчивает набор текущего токена).
По примерно тем же причинам до C++11 нельзя было писать что-то вроде
std::vector<std::vector<int>> v;потому что
>> в конце считывалось как operator>>, а не два закрывающихся шаблона -> приходилось ставить между ними пробел.Я уже чуть-чуть писал про most vexing parse. Давайте посмотрим ещё чуть-чуть.
Когда вы пишете вот такое в глобальном неймспейсе:
Type func(OtherType);вы сразу понимаете, что это объявление функции. Однако бывают более неочевидные (просто потому что вы имеете в виду другое) кейсы, когда компилятор так же воспринимает объявление:
class X {
public:
X() {}
X(Type obj) : obj(obj) {}
private:
Type obj();
};Вполне можно думать, что раз приватное и мы инициализируем, то это поле, но это так же объявление функции с именем
obj, из-за чего такой код не скомпилируется. Или более дикие примеры:
struct X {};
struct Y {
Y(const X&) {}
void f() {}
};
int main() {
Y y(X());
y.f();
}Код не скомпилируется на выражении
y.f(), из-за того, как мы инициализируем объект y: на самом деле это тоже объявление функции y, возвращающей Y и принимающей X. По-хорошему конечно использовать фигурные скобки для инициализации: Y y{X()}, — а можно поставить ещё одну пару скобок вокруг X: Y y((X())).Короч будьте внимательны и читайте ошибки компиляции.