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

* преобразования между целым типом и типом с плавающей точкой: приведение от любого типа с плавающей точкой к любому целому типу или наоборот;

* преобразования указателей: приведение целого значения 0 к типу указателя или трансформация указателя любого типа в тип void*;

* преобразования в тип booclass="underline" приведение от любого целого типа, типа с плавающей точкой, перечислимого типа или указательного типа к типу bool.

Вот несколько примеров:

extern void print( void* );

extern void print( double );

int main() {

int i;

print( i ); // соответствует print( double );

// i подвергается стандартному преобразованию из int в double

print( i ); // соответствует print( void* );

// i подвергается стандартному преобразованию

// из int* в void*

return 0;

}

Преобразования, относящиеся к группам 1, 2 и 3, потенциально опасны, так как целевой тип может и не обеспечивать представления всех значений исходного. Например, с помощью float нельзя адекватно представить все значения типа int. Именно по этой причине трансформации, входящие в эти группы, отнесены к категории стандартных преобразований, а не расширений типов.

int i;

void calc( float );

int main() {

calc( i ); // стандартное преобразование между целым типом и типом с

// плавающей точкой потенциально опасно в зависимости от

// значения i

return 0;

}

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

Предполагается, что все стандартные изменения требуют одного объема работы. Например, преобразование из char в unsigned char не более приоритетно, чем из char в double. Близость типов не принимается во внимание. Если две устоявших функции требуют для установления соответствия стандартной трансформации фактического аргумента, то вызов считается неоднозначным и помечается компилятором как ошибка. Например, если даны две перегруженные функции:

extern void manip( long );

extern void manip( float );

то следующий вызов неоднозначен:

int main() {

manip( 3.14 ); // ошибка: неоднозначность

// manip( float ) не лучше, чем manip( int )

return 0;

}

Константа 3.14 имеет тип double. С помощью того или иного стандартного преобразования соответствие может быть установлено с любой из перегруженных функций. Поскольку есть две трансформации, приводящие к цели, вызов считается неоднозначным. Ни одно преобразование не имеет преимущества над другим. Программист может разрешить неоднозначность либо путем явного приведения типа:

manip ( static_castlong( 3.14 ) ); // manip( long )

либо используя суффикс, обозначающий, что константа принадлежит к типу float:

manip ( 3.14F ) ); // manip( float )

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

extern void farith( unsigned int );

extern void farith( float );

int main() {

// каждый из последующих вызовов неоднозначен

farith( 'a' ); // аргумент имеет тип char

farith( 0 ); // аргумент имеет тип int

farith( 2uL ); // аргумент имеет тип unsigned long

farith( 3.14159 ); // аргумент имеет тип double

farith( true ); // аргумент имеет тип bool

}

Стандартные преобразования указателей иногда противоречат интуиции. В частности, значение 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.