С++ для начинающих




Подробнее о стандартном преобразовании - часть 3


}

Стандартные преобразования указателей иногда противоречат интуиции. В частности, значение 0 приводится к указателю на любой тип; полученный таким образом указатель называется нулевым. Значение 0 может быть представлено как константное выражение целого типа:

void set(int*);

int main() {

   // преобразование указателя из 0 в int* применяется к аргументам

   // в обоих вызовах

   set( 0L );

   set( 0x00 );

   return 0;

}

Константное выражение 0L (значение 0 типа long int) и константное выражение 0x00 (шестнадцатеричное целое значение 0) имеют целый тип и потому могут быть преобразованы в нулевой указатель типа int*.

Но поскольку перечисления не относятся к целым типам, элемент, равный 0, не приводим к типу указателя:

enum EN { zr = 0 };

set( zr );   // ошибка: zr нельзя преобразовать в тип int*

Вызов функции set() является ошибкой, так как не существует преобразования между значением zr элемента перечисления и формальным параметром типа int*, хотя zr равно 0.

Следует отметить, что константное выражение 0 имеет тип int. Для его приведения к типу указателя требуется стандартное преобразование. Если в множестве перегруженных функций есть функция с формальным параметром типа int, то именно в ее пользу будет разрешена перегрузка в случае, когда фактический аргумент равен 0:

void print( int );

void print( void * );

void set( const char * );

void set( char * );

int main () {

   print( 0 );   // вызывается print( int );

   set( 0 );     // неоднозначность

   return 0;

}

При вызове print(int) имеет место точное соответствие, тогда как для вызова print(void*) необходимо приведение значения 0 к типу указателя. Поскольку соответствие лучше преобразования, для разрешения этого вызова выбирается функция print(int). Обращение к set() неоднозначно, так как 0 соответствует формальным параметрам обеих перегруженных функций за счет применения стандартной трансформации. Раз обе функции одинаково хороши, фиксируется неоднозначность.

Последнее из возможных преобразований указателя позволяет привести указатель любого типа к типу void*, поскольку void* – это родовой указатель на любой тип данных. Вот несколько примеров:

#include <string>

extern void reset( void * );

void func( int *pi, string *ps ) {

   // ...

   reset( pi );   // преобразование указателя: int* в void*

   /// ...

   reset( ps );   // преобразование указателя: string* в void*

}

Только указатели на типы данных могут быть приведены к типу void* с помощью стандартного преобразования, с указателями на функции так поступать нельзя:

typedef int (*PFV)();

extern PFV testCases[10];   // массив указателей на функции

extern void reset( void * );

int main() {

   // ...

   reset( textCases[0] );  // ошибка: нет стандартного преобразования

                           // между int(*)() и void*

   return 0;

}




Содержание  Назад  Вперед