вторник, 13 ноября 2012 г.

С++ выражения

L-value, R-value и их обобщения.


    Как известно (например, отсюда), каждая переменная характеризуется двумя важными вещами - физическим адресом в памяти компьютера, где она размещена, и непосредственным значением, которое по этому адресу размещается.
 
    Именно с этим (как представляется мне) и связана спецификация типов данных, о которой можно прочесть, например,здесь.. Зная о том, что по адресу p размещено целое число, мы знаем, что через (16/32/64 - зависит от платформы) бита лежит следующее число.
 
    Обратимся к стандарту. Как известно и без него, прежде чем обратиться к какой либо сущности, мы должны ее определить (если мы только объявим некоторую сущность, то поведение программы при попытке доступа , как вы уже догадались, будет ошибочным).
Например, при определении 
Struct S;
мы объявляем структуру, но не говорим ничего конкретного о том, что эта структура из из себя представляет.
Напротив, следующее объявление (которое будет являться и определением), ясно говорит о том, что у переменная с именем i по адресу, например, 0х100 имеет значение 100.
int i=100;

    В связи с этим вот такое выражение имеет смысл в C++:
int i=100;
i+=20;
а такое - нет:
20+=100;
Важно понимать, что это - свойство выражений, а не объектов. Почему свойство выражений? Компилятор знает, что такое двадцать - целое число (которое может быть при необходимости приведено к другому типу), и прибавить двадцать по адресу определенной переменной может. Однако, компилятор не может прибавить ничего "просто" к абстрактному числу, которое не размещено нигде в памяти. Соответственно, первый случай - это пример так называемого lvalue, второй - rvalue.

    В стандарте указана более широкая спецификация выражений. Делятся они на gvalue(generalized lvalue) и rvalue.  Отличие от рассмотренной выше спецификации в том, что glvalue(впрочем, как и rvalue) включает и xvalue (eXpiring value) - объект, который близок к концу своей "жизни".

    В качестве хорошего примера можно привести следующий:

class Foo
{
    std::string name;

public:

    Foo(std::string some_name) : name(std::move(some_name))
    {
    }

    std::string& original_name()
    {
        return name;
    }

    std::string copy_of_name() const
    {
        return name;
    }
};
Тут: foo.copy_of_name() - prvalue (pure rvalue) - так как возвращает объект, foo.original_name() - lvalue, так как возвращает ссылку на объект и, наконец, std::move(some_name) - xvalue, так как возвращает ссылку на rvalue.

    Стандартный способ проверки на принадлежность к lvalue (кроме досконального знания стандарта) - задать себе вопрос - допустима ли операция взятия адреса у интересующего объекта. Если да - то это, очевидно, lvalue (не работает, если в классе объекта переопределена операция взятия адреса, но для меня это слишком техническая вещь)).
    

Комментариев нет:

Отправить комментарий