如果没有为 int 定义 sqrt(),为什么它可以在 int 变量上正常工作?

2022-01-23 00:00:00 gcc g++ c++ gcc4 gcc-4.2

Programming: Principles and Practice using C++第 3 章(第 6 次印刷)),Stroustrup 声明 (p.68):请注意,sqrt() 不是为 int 定义的".

这是一个基于该章节的简单 C++ 程序:

#include "std_lib_facilities.h"主函数(){诠释 n = 3;cout <<"n == 的平方根" <<sqrt(n) <<"
";}

鉴于上面的引用,我预计编译或运行此程序的过程会以某种方式失败.

令我惊讶的是,编译它(使用 g++ (GCC) 4.2.1)并运行它成功,没有错误或警告,并产生了以下完美的输出:

n == 1.73205 的平方根

因此我的问题是:如果 sqrt() 真的没有为 int 定义,那么为什么上面的程序不会以某种方式失败?

解决方案

更新2

这个问题与一个完全重复的问题合并,看看这个,实际答案比任何人最初想象的要简单得多.std_lib_facilities.h 的当前版本包括以下行:

inline double sqrt(int x) { return sqrt(double(x));}//匹配 C++0x

它为 int 情况创建了一个特定的重载,以匹配现代编译器应该执行的操作,即 将整数参数转换为 double,尽管此版本并未涵盖所有案例.

如果 std_lib_facilities.h 没有被使用,那么原始逻辑仍然适用,虽然 gcc-4.2 与 Visual Studio 2012 来自原始问题,但 4.1.2 版本使用 __builtin_sqrt 专门用于整数情况.

原创

自 2005 年左右起,标准草案要求将 integer 参数cast 转换为 double,这在C++ 标准草案.如果我们查看 26 Numerics library 部分,然后转到 26.8 C 库 部分,它涵盖了 float、double 和 long double 的数学函数重载8:

<块引用>

除了 中的数学函数的 double 版本外,C++ 还添加了这些函数的 float 和 long double 重载版本,具有相同的语义.

这对于 int 情况是 ambiguous 但标准要求提供足够的重载以便 integer 参数是 cast 到 double.它包含在第 11 段中,它说(强调我的):

<块引用>

此外,应有足够的额外重载以确保:

<块引用>

  1. 如果与 double 形参对应的任何算术实参具有 long double 类型,则与 double 形参对应的所有算术实参都将有效地转换为 long double.
  2. 否则,如果任何与 double 形参对应的算术实参具有 double 类型或整数类型,则所有与 double 形参对应的算术实参都被有效地强制转换为 double.
  3. 否则,所有与双精度参数对应的算术参数都具有浮点类型.

更新

正如@nos 指出的那样,被调用的 sqrt 版本可能来自 math.h 标头,而不是来自 cmath,如果是这种情况并且此处可能存在实现定义警告,那么我们可能会恢复到旧的C风格行为,如果唯一可用的版本是 sqrt(double) 这意味着 int 将被隐式转换为 double.

我发现在 gccclang 上进行测试的一种方法是对 a 使用 long 类型如果我们只有 sqrt(double) 可用,它与 -Wconversion 标志一起触发在我的平台上可能改变值的转换的警告.事实上,如果我包含 math.h 而不是 cmath 我们可以产生这个警告.虽然我无法在 clang 中触发此行为,这似乎表明这是 依赖于实现的.

In chapter 3 of Programming: Principles and Practice using C++ (sixth printing), Stroustrup states (p.68): "Note that sqrt() is not defined for an int".

Here is a simple C++ program based on that chapter:

#include "std_lib_facilities.h"

int main()
{
    int n = 3;
    cout << "Square root of n == " << sqrt(n) << "
";
}

Given the quote above, I would expect the process of compiling or running this program to fail in some way.

To my surprise, compiling it (with g++ (GCC) 4.2.1) and running it succeeded without errors or warnings, and produced the following perfectly decent output:

Square root of n == 1.73205

My question therefore is: if sqrt() really is not defined for an int, then why doesn't the program above fail somehow?

解决方案

Update 2

This question was merged with an exact duplicate, on taking a look at this, the actual answer is much simpler than anyone originally thought. The current version of std_lib_facilities.h includes the following line:

inline double sqrt(int x) { return sqrt(double(x)); }   // to match C++0x

which creates a specific overload for the int case to match what modern compilers should be be doing which is cast integer arguments to double, although this version does not cover all the cases.

If std_lib_facilities.h was not being used than the original logic still applies, although gcc-4.2 is rather old compared to Visual Studio 2012 from the original question but a 4.1.2 version have uses __builtin_sqrt specifically for the integer case.

Original

Since around 2005 the draft standard required integer argument to be cast to double this is covered in the draft C++ standard. If we look in section 26 Numerics library and then go to section 26.8 C library which covers the <cmath> header, it specifies overloads of the math functions for float, double and long double which is covered in paragraph 8:

In addition to the double versions of the math functions in , C++ adds float and long double overloaded versions of these functions, with the same semantics.

which would be ambiguous for the int case but the standard requires that sufficient overload are provided so that integer arguments are cast to double. It is covered in paragraph 11 which says(emphasis mine):

Moreover, there shall be additional overloads sufficient to ensure:

  1. If any arithmetic argument corresponding to a double parameter has type long double, then all arithmetic arguments corresponding to double parameters are effectively cast to long double.
  2. Otherwise, if any arithmetic argument corresponding to a double parameter has type double or an integer type, then all arithmetic arguments corresponding to double parameters are effectively cast to double.
  3. Otherwise, all arithmetic arguments corresponding to double parameters have type float.

Update

As @nos points out it is possible that the version of sqrt being called is from math.h header as opposed to the overloads from cmath, if that is the case and there is likely a implementation defined caveat here then we may be reverting to old C style behavior if the only version available is sqrt(double) which would mean that int would be converted to double implicitly.

One way I found to test this on gcc and clang would be to use long type for a which along with -Wconversion flag triggers a warning for a potentially value altering conversion on my platform if we only have sqrt(double) available. Indeed if I include math.h instead of cmath we can produce this warning. Although I can not trigger this behavior in clang which seems to indicate this is implementation dependent.

相关文章