В главе 3 Программирование: принципы и практика с использованием C ++ (Шестая печать), Страуструп сообщает (стр.68): «Обратите внимание, что sqrt()
не определено для int
«.
Вот простая программа на C ++, основанная на этой главе:
#include "std_lib_facilities.h"
int main()
{
int n = 3;
cout << "Square root of n == " << sqrt(n) << "\n";
}
Учитывая приведенную выше цитату, я ожидаю, что процесс компиляции или запуска этой программы каким-то образом завершится неудачно.
К моему удивлению, его компиляция (с g ++ (GCC) 4.2.1) и его запуск прошли успешно без ошибок и предупреждений и дали следующий вполне приличный результат:
Square root of n == 1.73205
Поэтому мой вопрос: если sqrt()
на самом деле не определено для int
, тогда почему вышеприведенная программа как-то не работает?
Обновление 2
Этот вопрос был объединен с точной копией, при взгляде на него фактический ответ намного проще, чем кто-либо первоначально думал. Текущая версия std_lib_facilities.h включает в себя следующую строку:
inline double sqrt(int x) { return sqrt(double(x)); } // to match C++0x
который создает определенную перегрузку для ИНТ случай, чтобы соответствовать тому, что должны делать современные компиляторы, который приведите целочисленные аргументы к удвоению, хотя эта версия не охватывает все случаи.
Если std_lib_facilities.h не был использован, чем оригинальная логика все еще применяется, хотя gcc-4.2
довольно старый по сравнению с Visual Studio 2012 от первоначального вопроса, но 4.1.2
версия имеет использование __builtin_sqrt
специально для целочисленного случая.
оригинал
Так как вокруг 2005 требуется проект стандарта целое число аргумент быть бросать в двойной это покрыто в проект стандарта C ++. Если мы посмотрим в разделе 26
Библиотека чисел а затем перейти в раздел 26.8
C библиотека который охватывает <cmath>
заголовок, он определяет перегрузки математических функций для поплавок, двойной а также длинный двойной который описан в пункте 8:
В дополнение к двойным версиям математических функций в C ++ добавляются плавающие и длинные двойные перегруженные версии этих функций с одинаковой семантикой.
который был бы ambiguous
для int
случай, но стандарт требует, чтобы была обеспечена достаточная перегрузка, чтобы целое число аргументы бросать в двойной. Это покрыто в пункте 11 который говорит (акцент мой):
Кроме того, должны быть дополнительные перегрузки, достаточные для обеспечения:
- Если какой-либо арифметический аргумент, соответствующий параметру double, имеет тип long double, то все арифметические аргументы, соответствующие параметрам double, эффективно приводятся к long double.
- В противном случае, если какой-либо арифметический аргумент, соответствующий параметру double, имеет тип double или целочисленный тип, тогда все арифметические аргументы, соответствующие двойным параметрам, эффективно приводятся к двойным.
- В противном случае все арифметические аргументы, соответствующие двойным параметрам, имеют тип float.
Обновить
Как указывает @nos, возможно, что версия sqrt
вызывается из math.h
заголовок в отличие от перегрузок из cmath
, если это так и есть вероятность реализация определена будьте осторожны может возвращаться к старому Стиль С поведение, если единственная доступная версия sqrt(double)
что означало бы, что ИНТ будет преобразован в двойной неявно.
Один способ, который я нашел, чтобы проверить это на gcc
а также clang
будет использовать долго тип для a
который наряду с -Wconversion
флаг вызывает предупреждение о потенциально изменяющем значение преобразовании на моей платформе, если у нас есть только sqrt(double)
имеется в наличии. Действительно, если я включу math.h
вместо cmath
мы можем произвести это предупреждение. Хотя я не могу вызвать это поведение в лязг который, кажется, указывает на то, что это зависит от реализации.
10 неявно преобразуется в двойной. Это произойдет автоматически, если у вас есть правильный прототип функции для sqrt.
Редактировать: побитый комментариями
Потому что неявные преобразования. sqrt
определяется для double
и int
значение может быть (и является) неявно преобразовано в значение типа double
,
(На самом деле это довольно трудно предотвращать функция, которая принимает double
от вызова с int
, Вы можете заставить свой компилятор выдавать предупреждение, но так как это обычно сохраняющее значение преобразование, даже это может быть сложно. C ++ наследует от C дизайн, чтобы как можно больше стараться, чтобы код работал, даже если для этого требуются искажения. Другие языки намного строже в подобных вещах.)
sqrt
определяется для double
, И C ++ позволяет конвертировать int
в double
неявно.
int n = 3;
double x = sqrt(n); // implicit conversion of n from int to double
Неявные преобразования могут произойти, когда вы используете значение в качестве параметра функции или присваиваете его переменной.
Пример для второго случая будет:
int n = 3;
double x = n; // implicit conversion of n from int to double
Обратите внимание, что операторы также являются просто функциями. Таким образом, вы также можете добавить int
к double
, который преобразует int
к double
перед вызовом фактического добавления:
int n = 3;
double x = 1.0;
double sum = n + x; // implicit conversion of n from int to double
Потому что есть неявное преобразование из int
в double
,
С преобразованием ваш код будет выглядеть так:
cout << "Square root of n == " << sqrt((double)n) << "\n";
Поскольку компилятор фактически автоматически (то есть «неявно») преобразует целое число в double
(или, может быть long double
) и отправив это значение sqrt()
, Это совершенно нормально и совершенно законно.