Skip to content

Latest commit

 

History

History
96 lines (72 loc) · 4.9 KB

C++19 - Move.md

File metadata and controls

96 lines (72 loc) · 4.9 KB

11.4 Formal definition of value categories

Вначале говорили про присваивание/не присваивание, или можно взять адрес или нет.

В одну стоону это неверно в таком примере:

const int x = 5;
x = 4;

В другую сторону: когда делаешь vb[5] получается временный bit-reference которому присваивается значение (почти как string() = ...;)

Первое заблуждение про мув-семантику: rvalue и lvalue - свойство объектов. Это не так, потому что это почти синтаксическое свойство - потому что это к выражению относится. Можно взять конкретную строчку кода и спросить это rvalue или lvalue, объект может участвовать в выражениях обоих типов, и поэтому это не свойство объекта.

Expression:

  1. lvalue 1.1. identifier - последовательность символов, имя переменной
    1.2. =,op= присваивание стандартных типов
    1.3. prefix ++ --
    1.4. unary * разыменовывание
    1.5. ?: (тернарный оператор) - если оба выражения lvalue
    1.6. comma - если правый операнд lvalue
    1.7. function call if return value is lvalue-reference(f)
    1.8. cast-expression same rool for 1.7
  2. rvalue
    2.1. literal - последовательность символов
    2.2. +,-,* etc результаты операторов
    2.3. postfix ++ --
    2.4. unary & + -
    2.5. ?: если хоть одно rvalue (потмоу что на момент компиляции)
    2.6. commma - если правый операнд rvalue
    2.7. function call if return value is not reference or is rvalue-ref(&&)
    2.8. cast-expression same rool for 2.7

11.5. Rules of reference initialization

int a = 1;
int& b = a;
int& c = 2; // CE

lvalue-reference нельзя инициализировать с помощью rvalue выражения (если не константная ссылка, иначе там продление работает) При этом rvalue-reference можно инициализировать только rvalue-значениями.

Можно писать так:

int&& e = 5;
int& g = e;

Просто потому что e - это identifier, поэтому lvalue по определению.

У константных ссылок работают обычные правила с константностью, единственное отличие - const lvalue-reference можно биндить к rvalue.

11.6. Universal references

void f(int& x); только lvalue
void f(int&& x); только rvalue
Будет перегрузка от lvalue/rvalue. А как сделать чтобы можно было по ссылке принимать оба вида value?

template <typename T>
void g(T&& x);

Такая функция будет принимать как rvalue так и lvalue.

Если шаблонная функция имеет типом параметра T&& где T - тип ее шаблонного аргумента, то такая функция может принимать как rvalue и lvalue. Синтаксис как выше в примере. При этом должно быть ровно как там (с шаблном, без конста и других типов)

type(expr) = int&&, expr - rvalue => T = int, type(x) = int&&
Но что если так?
type(expr) = int&, expr - lvalue => T = int&, type(x) = int&
Неочевидно, потому что type(x) = int&&&, и но & + && = &

Арифметика такая:
& + & = &
& + && = &
&& + & = &
&& + && = &&

type(expr) = int, expr = lvalue, T = int&, type(x) = int&
Компилятор специально добавляет & к Т, чтобы оп арифметике получить type(x) = int&

А что с функцией std::move? Мы хотим чтобы он трактовался как rvalue. Он собственно говоря так и делает - трактует lvalue как rvalue.

tempalte <typename T>
std::remove_Reference_t<T>&& move(T&& x) noexcept {
    return static_cast<std::remove_reference_t<T>&&>(x);
}

Такое странное возвращаемое значение, чтобы в любом случае вернуть T&&, потому что если бы x был int&, то по арифметике убралось &&. Поэтому мы убираем все &, и навешиваем &&