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.
Следует отметить, что константное выражение 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;
}