expression - синтаксическая конструкция, которая состоит из идентификаторов и литералов, соединенных операторами и скобочками все инструкции бывают либо
- expressions
- declaration (using-и), должны быть в блок скоупе или в глобальной области
- control statement - (ифы, форы, вайлы)
lvalue/rvalue - характеристика экспрешена в си было так, что lvalue - то, что может стоять слева от =, rvalue - всё, что не lvalue в C++ lvalue это то, что лежит в памяти glvalue - то, что лежит в памяти (lvalue) или имеет временное расположение (xvalue), rvalue - это те, которые не имеют расположения в памяти (prvalue) или имеют временное расположение (xvalue)
(x=y)=z при (x=y) возвращается ссылка, на то, что получилось (х), lvalue результаты операторов присваивания - lvalue lvalue можно что-то присваивать rvalue a++; lvalue ++a; возвращаемый тип префиксного инкремента это ссылка на объект ++a++ по стандарту постфиксные операции приоритетнее, поэтому так нельзя писать
результатом тернарного оператора будет lvalue только если оба возвращаемых объекта lvalue, иначе в любом случае rvalue тип результата тернарного оператора вычисляется на этапе компиляции
операторы, возвращающие lvalue: разыменование, префиксный инкремент/декремент, присваивание, []
оператор запятая возвращает правое значение
(a,b) = 1 тоже самое что b = 1;
delete p,pp == delete p
delete (p,pp) == delete pp
результат sizeof() это constexpr
имя переменной вступает в действие сразу после = т.е. int x = x; - корректно (== int x;) однако это UB, тк имя то действительное, но объект lifetime не начал
void func(int a) = delete; запрещает вызов функции с такими аргументами (даёт CE)
к rvalue своих типов данных можно присваивать, если не определить это иначе (справа от перегрузки добавить &)
если есть метод, который возвращает ссылку на поле, то его стоит пометить &, чтоб вызывался только от lvalue, чтоб избежать dangling references
int& x = S{}.x - UB

вектор буль - пример того, когда rvalue что-то нужно присваивать. оператор[] вектора буль возвращает новый временный объект (rvalue - ему не соответствует никакая переменная в памяти), присваивание к которому изменяет объект вектора
assert() - function-like макрос, который вызывает std::abort в runtime, если условие ложно в релиз билде исчезает static_assert(constexpr) - функция, которая в compile time кидает CE, если ей передали false
(3+2)++ - некоректно, потому что инкремент можно вызывать только у lvalue, а результат + - rvalue
formal definitions of lvalue and rvalue
lvalue и rvalue - виды экспрешенов, а не типов!!! каждый экспрешен обладает 2 независимыми свойствами - тип и вид value int&& - никакое не value, это тип
lvalue
- любая переменная, не важно какого типа
- строковый литерал (“abc”)
- все операторы присваивания над фундаментальными типами
- префиксный инкремент/декремент над фундаментальными типами
- если после разыменования получается фундаментальный тип
- если получается фундаментальный тип после []
- результат вызова функции - T&
- результат каста, если каст был к T&
вид value результата оператора запятой - вид value её правой части вид value результата тернарного оператора: обе части lvalue - lvalue одна из частей rvalue - rvalue обе части rvalue - rvalue тк в compile time нужно решить вид всего выражения, поэтому берется “худший случай”
rvalue
- литерал (‘a’, 42, 2.0f, true)
- все арифметические операторы и операторы сравнения над фундаментальными типами
- постфиксный инкремент/декремент над фундаментальными типами
- унарный & (&x)
- результат вызова функции T или T&&
- результат каста, если каст был к T либо к T&&
T&& - такая ссылка, которая ведёт себя как обычная ссылка, но если её вернуть из функции, то это будет считаться rvalue переменные типа T&& - всё равно lvalue