c++gccg++gcc4gcc-4.2

Why does sqrt() work fine on an int variable if it is not defined for an int?


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) << "\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?


Solution

  • 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.