пятница, 22 июня 2012 г.

Категории значений С++

Все выражения делятся на следующие категории значений:
            Каждое выражение принадлежит ровно одной категории значений: lvalue, xvalue или prvalue. В процессе вычисления, выражение может переходить из одной категории значений в другую, но все выражения, в конечном итоге, преобразовываются к категории значений rvalue.
            Например: выражение может сначала быть lvalue, после чего преобразуется к rvalue.

            В общем, все категории значений можно разделить на два видалевосторонние (изменяемые) и правосторонние (не изменяемые).

операнд_1 = операнд_2;

Где:
Операнд-1 – левостороннее значение (glvalue, lvalue, xvalue – ссылка на правосторонние данные).  
Операнд-2 – левостороннее значение или правостороннее значение (rvalue, prvalue, xvalue – результат выражения с prvalue или ссылками на rvalue).


Общий пример.

int f0() { int i = 23; return i; }
int& f1() { int i = 23; return i; }

int main()
{
   int p = 5;
   //&5;                         // Ошибка. Выражение должно быть lvalue.
   &p;                           // Ок.
   int *t = &p;                  // Ок.
   &t;                           // Ок.
   //&(double)p;         // Ошибка. Выражение должно быть lvalue.
   &(double&)p;                  // Ок.
   double *x = &*(double*)&p;    // Ок.
   cout << *(int*)x << endl;
   //&f0();                      // Ошибка. Выражение должно быть lvalue.
   cout << &f1() << endl;        // Ок.
   return 0;
}
lvalue
1.     lvalue назван так исторически, так как он ​​может находиться с левой стороны в операциях присвоения.  lvalue (левосторонние данные) – данные, которым можно присвоить какое-либо значение и адрес расположения этих данных в памяти можно получить используя оператор «&». 

К левосторонним данным относятся.
1.     Переменные:                   int b = 5; int temp; temp = b;

2.     Ссылка (в момент явного приведения с созданием временной ссылки ((тип&)lvalue)):                                int a = 5; (double&)a = 23.23;

3.     Раскрытый указатель / адрес:      int *p = new int(23);    *p = 5;
int a = 5; *(double*)&a = 23.23; // Извлекаем адрес, по которому расположена переменная-а, приводим этот адрес к типу double*, раскрываем этот адрес, после чего по этому же адресу неявно создается временная переменная типа double, которой присваивается константна. 

4.     Нераскрытый указатель, если ему присваивается адрес:
int main() 
{
   int a = 23;  int *p = new int(); int *q = new int();
   p = q;                        // Присваиваем адрес, присвоенный указателю q.
   p = &a;                       // Присваиваем адрес переменной.
   p = new int(5);               // Присваиваем адрес выделенной памяти.
   return 0;
}

5.     Возвращаемое значение функции по ссылке, указателю; в этом случае, на месте инициатора вызова функции, будет подставлено левостороннее значение.
int& f1(int& a)          { return a;  }                    // Возвращает ссылочную переменную.
int* f2(int& a)          { return &a; }                    // Возвращает адрес, на который ссылается ссылка.
int*& f3(int*& a)        { return a;  }                    // Возвращает ссылочную переменную-указатель.

int main()
{
   int a = 5, b = 10, temp;
   int *p = new int(23);
   cout << "a == " << a << " " << ", b == " << b << ", *p == " << *p << endl;
   temp  = a;              
   f1(a) = b;                     // f1(a) - левостороннее значение.
   *(f2(b)) = temp;               // f2(b) - правостороннее значение. *(f2(b)) - левостороннее значение.
   p = &temp;                     // Имя указателя - левостороннее значение.
   f3(p) = &temp;                 // f3(p) - левостороннее значение.
   cout << "a == " << a << " " << ", b == " << b << ", *p == " << *p << endl;  
   return 0;
}

rvalue
1.     r-value назван так исторически, так как он ​​может находиться с правой стороны в операциях присвоения. 
2.     r-value (правосторонние данные) – это данные, которые можно присвоить левосторонним данным и адрес расположения этих данных в памяти нельзя получить используя оператор «&».
3.     Результатом всех выражений всегда есть rvalue



К правосторонним данным относятся.

1.     Результат применение оператора &.
int a = 23;
&a;              // Результат взятия адреса - правостороннее значение.
//&a = 5;        // Недопустимо: попытка выполнить присвоение правосторонним данным.

2.     Приведение значения дает r-value.
int main()
{
 int i = 5;
 //(double)i = 23.23; // Ошибка. (double)i - приведение значения дает r-value.
 return 0;
}

Исключение (VS2012): приведение значения к своему же типу дает l-value.
 
int main()
{
   int x = 23;
   const int &s = x;
   (int)s = 32;
   //(unsigned)s = 32; // Ошибка. Результатом приведения к другому типу является r-value.
   cout << x << endl;
   return 0;
}

3.     Результат вызова функции, тип возвращаемого значения которой не является ссылка.

int r()                                   // Возвращает rvalue
{ return 0; }


int f() { int i = 23; return i; }

int main()
{
    int a = f();     // Ок. f() возвращает правостороннее значение, может быть присвоено другим данным.
    f();             // Эквивалентно: 23; - чистое правостороннее значение.
    //f() = 5;       // Недопустимо: попытка выполнить присвоение чистым правосторонним данным.       
    return 0;
}
xvalue
xvalue («eXpiring» - «разрушающееся» значение) ссылается на объект, как правило, ближе к концу его жизни. xvalue относится к категории glvalue только в том случае, если glvalue преобразуется в выражении к xvalue, во всех остальных случаях xvalue относиться к категории rvalue.


1. xvalue является результатом определенных видов выражений, содержащих rvalue ссылки.

int&& i = 5 + 5;         //5 + 5 создает xvalue == 10
i;                       //iglvalue, т.к. к нему можно применить оператор &.

2. Результат преобразования rvalue к ссылке на правосторонние данные есть xvalue, полученное из категории rvalue.

(int&&)23.23;  // rvalue преобразуется к xvalue.


3. Результат вызова функции, тип возвращаемого значения которой является ссылка на rvalue, считается xvalue.

int&& x()                                 // Возвращает xvalue
{
        return 0;
        return 0 + 0;
}
glvalue
glvalue («generalized» - «обобщенное» lvalue) является lvalue или xvalue. К glvalue можно применить оператор «&».


1. Результат преобразования glvalue к ссылке на правосторонние данные есть xvalue, полученное из категории glvalue.

int i = 0;
(int&&)i;      // glvalue преобразуется к xvalue.


2. Создание ссылки на правосторонние данные:

int&& i = 5;             // 5 – prvalue.
i;                       // iglvalue, т.к. к нему можно применить оператор &.

prvalue
prvalue («pure» «чистая» rvalue) – значение констант.
1.   Константы:                 
23;
7.3E
true