Выбрать главу

* можно получить значение объекта;

* это значение легко модифицировать (если только в объявлении объекта нет спецификатора const).

Напротив, r-значение – это выражение, значение которого вычисляется, или выражение, обозначающее временный объект, для которого нельзя получить адрес и значение которого нельзя модифицировать. Вот простой пример:

int calc( int );

int main() {

int lval, res;

lval = 5; // lvalue: lval; rvalue: 5

res = calc( lval );

// lvalue: res

// rvalue: временный объект для хранения значения,

// возвращаемого функцией calc()

return 0;

}

В первом операторе присваивания переменная lval – это l-значение, а литерал 5 – r-значение. Во втором операторе присваивания res – это l-значение, а временный объект, в котором хранится результат, возвращаемый функцией calc(), – это r-значение.

В некоторых ситуациях в контексте, где ожидается значение, можно использовать выражение, представляющее собой l-значение:

int obj1;

int obj2;

int main() {

// ...

int local = obj1 + obj2;

return 0;

}

Здесь obj1 и obj2 – это l-значения. Однако для выполнения сложения в функции main() из переменных obj1 и obj2 извлекаются их значения. Действие, состоящее в извлечении значения объекта, представленного выражением вида l-значение, называется преобразованием l-значения в r-значение.

Когда функция ожидает аргумент, переданный по значению, то в случае, если аргумент является l-значением, выполняется его преобразование в r-значение:

#include string

string color( "purple" );

void print( string );

int main() {

print( color ); // точное соответствие: преобразование lvalue

// в rvalue

return 0;

}

Так как аргумент в вызове print(color) передается по значению, то производится преобразование l-значения в r-значение для извлечения значения color и передачи его в функцию с прототипом print(string). Однако несмотря на то, что такое приведение имело место, считается, что фактический аргумент color точно соответствует объявлению print(string).

При вызове функций не всегда требуется применять к аргументам подобное преобразование. Ссылка представляет собой l-значение; если у функции есть параметр-ссылка, то при вызове функция получает l-значение. Поэтому к фактическому аргументу, которому соответствует формальный параметр-ссылка, описанное преобразование не применяется. Например, пусть объявлена такая функция:

#include list

void print( listint );

В вызове ниже li – это l-значение, представляющее объект listint, передаваемый функции print():

listint li(20);

int main() {

// ...

print( li ); // точное соответствие: нет преобразования lvalue в

// rvalue

return 0;

}

Сопоставление li с параметром-ссылкой считается точным соответствием.

Второе преобразование, при котором все же фиксируется точное соответствие, – это преобразование массива в указатель. Как уже отмечалось в разделе 7.3, параметр функции никогда не имеет тип массива, трансформируясь вместо этого в указатель на его первый элемент. Аналогично фактический аргумент типа массива из NT (где N – число элементов в массиве, а T – тип каждого элемента) всегда приводится к типу указателя на T. Такое преобразование типа фактического аргумента и называется преобразованием массива в указатель. Несмотря на это, считается, что фактический аргумент точно соответствует формальному параметру типа “указатель на T”. Например:

int ai[3];

void putValues(int *);

int main() {

// ...

putValues(ai); // точное соответствие: преобразование массива в

// указатель

return 0;

}

Перед вызовом функции putValues() массив преобразуется в указатель, в результате чего фактический аргумент ai (массив из трех целых) приводится к указателю на int. Хотя формальным параметром функции putValues() является указатель и фактический аргумент при вызове преобразован, между ними устанавливается точное соответствие.

При установлении точного соответствия допустимо также преобразование функции в указатель. (Оно упоминалось в разделе 7.9.) Как и параметр-массив, параметр-функция становится указателем на функцию. Фактический аргумент типа “функция” также автоматически приводится к типу указателя на функцию. Такое преобразование типа фактического аргумента и называется преобразованием функции в указатель. Хотя трансформация производится, считается, что фактический аргумент точно соответствует формальному параметру. Например: