С.Б. Липпман, Ж. Лажойе - Язык программирования С++ Вводный курс (1114944), страница 74
Текст из файла (страница 74)
К аргументу могут быть примененынекоторые тривиальные преобразования, а именно:•преобразование l-значения в r-значение;•преобразование массива в указатель;•преобразование функции в указатель;•преобразования спецификаторов.(Подробнее они рассмотрены ниже.)Категория соответствия с преобразованием типа является наиболее сложной. Необходиморассмотреть несколько видов такого приведения: расширение типов (promotions),стандартные преобразования и определенные пользователем преобразования.(Расширения типов и стандартные преобразования изучаются в этой главе.Определенные пользователем преобразования будут представлены позднее, последетального рассмотрения классов; они выполняются конвертером, функцией-членом,которая позволяет определить в классе собственный набор “стандартных”трансформаций. В главе 15 мы познакомимся с такими конвертерами и с тем, как онивлияют на разрешение перегрузки функций.)С++ для начинающих439При выборе лучшей из устоявших функций для данного вызова компилятор ищетфункцию, для которой применяемые к фактическим аргументам преобразованияявляются “наилучшими”.
Преобразования типов ранжируются следующим образом:точное соответствие лучше расширения типа, расширение типа лучше стандартногопреобразования, а оно, в свою очередь, лучше определенного пользователемпреобразования. Мы еще вернемся к ранжированию в разделе 9.4, а пока на простыхпримерах покажем, как оно помогает выбрать наиболее подходящую функцию.9.3.1. Подробнее о точном соответствииСамый простой случай возникает тогда, когда типы фактических аргументов совпадают стипами формальных параметров.
Например, есть две показанные ниже перегруженныефункции max(). Тогда каждый из вызовов max() точно соответствует одному изint max( int, int );double max( double, double );int i1;void calc( double d1 ) {max( 56, i1 );// точно соответствует max( int, int );max( d1, 66.9 ); // точно соответствует max( double, double );объявлений:}Перечислимый тип точно соответствует только определенным в нем элементамenum Tokens { INLINE = 128; VIRTUAL = 129; };Tokens curTok = INLINE;enum Stat { Fail, Pass };extern void ff( Tokens );extern void ff( Stat );extern void ff( int );int main() {ff( Pass );ff( 0 );ff( curTok );// ...// точно соответствует ff( Stat )// точно соответствует ff( int )// точно соответствует ff( Tokens )перечисления, а также объектам, которые объявлены как принадлежащие к этому типу:}Выше уже упоминалось, что фактический аргумент может точно соответствоватьформальному параметру, даже если для приведения их типов необходимо некотороетривиальное преобразование, первое из которых – преобразование l-значения в rзначение.
Под l-значением понимается объект, удовлетворяющий следующим условиям:•можно получить адрес объекта;•можно получить значение объекта;С++ для начинающих•440это значение легко модифицировать (если только в объявлении объекта нетспецификатора const).Напротив, r-значение – это выражение, значение которого вычисляется, или выражение,обозначающее временный объект, для которого нельзя получить адрес и значениеint calc( int );int main() {int lval, res;lval = 5;//res = calc( lval//////return 0;lvalue: lval; rvalue: 5);lvalue: resrvalue: временный объект для хранения значения,возвращаемого функцией calc()которого нельзя модифицировать.
Вот простой пример:}В первом операторе присваивания переменная lval – это l-значение, а литерал 5 – rзначение. Во втором операторе присваивания res – это l-значение, а временный объект, вкотором хранится результат, возвращаемый функцией calc(), – это r-значение.В некоторых ситуациях в контексте, где ожидается значение, можно использоватьint obj1;int obj2;int main() {// ...int local = obj1 + obj2;return 0;выражение, представляющее собой l-значение:}Здесь obj1 и obj2 – это l-значения.
Однако для выполнения сложения в функции main()из переменных obj1 и obj2 извлекаются их значения. Действие, состоящее в извлечениизначения объекта, представленного выражением вида l-значение, называетсяпреобразованием l-значения в r-значение.Когда функция ожидает аргумент, переданный по значению, то в случае, если аргумент#include <string>string color( "purple" );void print( string );int main() {print( color );// точное соответствие: преобразование lvalue// в rvaluereturn 0;является l-значением, выполняется его преобразование в r-значение:}С++ для начинающих441Так как аргумент в вызове print(color) передается по значению, то производитсяпреобразование l-значения в r-значение для извлечения значения color и передачи его вфункцию с прототипом print(string).
Однако несмотря на то, что такое приведениеимело место, считается, что фактический аргумент color точно соответствуетобъявлению print(string).При вызове функций не всегда требуется применять к аргументам подобноепреобразование. Ссылка представляет собой l-значение; если у функции есть параметрссылка, то при вызове функция получает l-значение. Поэтому к фактическому аргументу,которому соответствует формальный параметр-ссылка, описанное преобразование не#include <list>применяется.
Например, пусть объявлена такая функция:void print( list<int> & );В вызове ниже li – это l-значение, представляющее объект list<int>, передаваемыйlist<int> li(20);int main() {// ...print( li );// точное соответствие: нет преобразования lvalue в// rvaluereturn 0;функции print():}Сопоставление li с параметром-ссылкой считается точным соответствием.Второе преобразование, при котором все же фиксируется точное соответствие, – этопреобразование массива в указатель.
Как уже отмечалось в разделе 7.3, параметрфункции никогда не имеет тип массива, трансформируясь вместо этого в указатель на егопервый элемент. Аналогично фактический аргумент типа массива из NT (где N – числоэлементов в массиве, а T – тип каждого элемента) всегда приводится к типу указателя наT. Такое преобразование типа фактического аргумента и называется преобразованиеммассива в указатель.
Несмотря на это, считается, что фактический аргумент точноint ai[3];void putValues(int *);int main() {// ...putValues(ai);// точное соответствие: преобразование массива в// указательreturn 0;соответствует формальному параметру типа “указатель на T”. Например:}Перед вызовом функции putValues() массив преобразуется в указатель, в результатечего фактический аргумент ai (массив из трех целых) приводится к указателю на int.С++ для начинающихХотя формальным параметром функции putValues() является указатель и фактическийаргумент при вызове преобразован, между ними устанавливается точное соответствие.При установлении точного соответствия допустимо также преобразование функции вуказатель.
(Оно упоминалось в разделе 7.9.) Как и параметр-массив, параметр-функциястановится указателем на функцию. Фактический аргумент типа “функция” такжеавтоматически приводится к типу указателя на функцию. Такое преобразование типафактического аргумента и называется преобразованием функции в указатель. Хотятрансформация производится, считается, что фактический аргумент точно соответствуетint lexicoCompare( const string &, const string & );typedef int (*PFI)( const string &, const string & );void sort( string *, string *, PFI );string as[10];int main(){// ...sort( as,as + sizeof(as)/sizeof(as[0] - 1 ),lexicoCompare// точное соответствие// преобразование функции в указатель);return 0;формальному параметру. Например:}Перед вызовом sort() применяется преобразование функции в указатель, котороеприводит аргумент lexicoCompare от типа “функция” к типу “указатель на функцию”.Хотя формальным параметром функции является указатель, а фактическим – имяфункции и, следовательно, было произведено преобразование функции в указатель,считается, что фактический аргумент точно третьему формальному параметру функцииsort().Последнее из перечисленных выше – это преобразование спецификаторов.
Оно относитсятолько к указателям и заключается в добавлении спецификаторов const или volatileint a[5] = { 4454, 7864, 92, 421, 938 };int *pi = a;bool is_equal( const int * , const int * );void func( int *parm ) {// точное соответствие между pi и parm: преобразование спецификаторовif ( is_equal( pi, parm ) )// ...return 0;(или обоих) к типу, который адресует данный указатель:}442С++ для начинающих443Перед вызовом функции is_equal() фактические аргументы pi и parm преобразуютсяиз типа “указатель на int” в тип “указатель на const int”.