Почему sqrt () отлично работает с переменной int, если она не определена для int?

В главе 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, тогда почему вышеприведенная программа как-то не работает?

7

Решение

Обновление 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 который говорит (акцент мой):

Кроме того, должны быть дополнительные перегрузки, достаточные для обеспечения:

  1. Если какой-либо арифметический аргумент, соответствующий параметру double, имеет тип long double, то все арифметические аргументы, соответствующие параметрам double, эффективно приводятся к long double.
  2. В противном случае, если какой-либо арифметический аргумент, соответствующий параметру double, имеет тип double или целочисленный тип, тогда все арифметические аргументы, соответствующие двойным параметрам, эффективно приводятся к двойным.
  3. В противном случае все арифметические аргументы, соответствующие двойным параметрам, имеют тип float.

Обновить

Как указывает @nos, возможно, что версия sqrt вызывается из math.h заголовок в отличие от перегрузок из cmath, если это так и есть вероятность реализация определена будьте осторожны может возвращаться к старому Стиль С поведение, если единственная доступная версия sqrt(double) что означало бы, что ИНТ будет преобразован в двойной неявно.

Один способ, который я нашел, чтобы проверить это на gcc а также clang будет использовать долго тип для a который наряду с -Wconversion флаг вызывает предупреждение о потенциально изменяющем значение преобразовании на моей платформе, если у нас есть только sqrt(double) имеется в наличии. Действительно, если я включу math.h вместо cmath мы можем произвести это предупреждение. Хотя я не могу вызвать это поведение в лязг который, кажется, указывает на то, что это зависит от реализации.

4

Другие решения

10 неявно преобразуется в двойной. Это произойдет автоматически, если у вас есть правильный прототип функции для sqrt.

Редактировать: побитый комментариями

5

Потому что неявные преобразования. sqrt определяется для doubleи int значение может быть (и является) неявно преобразовано в значение типа double,

(На самом деле это довольно трудно предотвращать функция, которая принимает double от вызова с int, Вы можете заставить свой компилятор выдавать предупреждение, но так как это обычно сохраняющее значение преобразование, даже это может быть сложно. C ++ наследует от C дизайн, чтобы как можно больше стараться, чтобы код работал, даже если для этого требуются искажения. Другие языки намного строже в подобных вещах.)

3

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
2

Потому что есть неявное преобразование из int в double,

С преобразованием ваш код будет выглядеть так:

cout << "Square root of n == " << sqrt((double)n) << "\n";
1

Поскольку компилятор фактически автоматически (то есть «неявно») преобразует целое число в double (или, может быть long double) и отправив это значение sqrt(), Это совершенно нормально и совершенно законно.

1
По вопросам рекламы [email protected]