So, I've dutifully not (for once) coded "using namespace std;" and I've dutifully (for once) stuck "std::" in front of a standard library routine so as not to have an accidental name clash with one of my own functions.
So, which square root function will run from main() and why?
1 2 3 4 5 6 7 8 9 10 11 12 13
#include <iostream>
#include <cmath>
double sqrt( double x )
{
std::cout << "Go away, I'm gonna run my own function on " << x << " if I want to! ";
return -999999;
}
int main()
{
std::cout << "The square root of 4 is ... " << std::sqrt( 4.0 ) << '\n';
}
(If your compiler complains then you can make the function definition double sqrt( double x ) throw()
It doesn't change the outcome.)
Strange. My implementation has std::sqrt(float) and std::sqrt(long double), but not std::sqrt(double). Does anyone know which overloads are required by the standard? I'm wondering if it counts as an implementation bug.
It is unspecified whether these names [...] are first declared within the global namespace scope and are then injected into namespace std by explicit using-declarations.
It so happens that the implementation(s) you've tried it on have <cmath> work as if:
1 2 3 4
#include <math.h>
namespace std {
using sqrt = ::sqrt;
}
...which of course explicit, proper use of std:: qualifiers won't save you from, since you've just provided a definition of ::sqrt that matches the declaration provided in <math.h>.
So, simply prepending std:: won't save me from name clashes - I would explicitly have to put my own functions in their own namespaces? That is almost never done in this forum (although I suspect it would be done at work).
I tried it on quite a few implentations ... all with the same result.
The issue arose from a std::sin() function used in a different thread.
I had to follow down several layers of includes to get to the declaration of sqrt(long double). This and the overloads seem to be declared globally and then injected into the std:: namespace, so I assume the complier is just matching the definition provided with the declaration (global via the std:: injection).
I believe the standard say something about "It is unspecified whether these names are first declared within the global namespace scope and are then injected into namespace std."
Well for the original code, it uses the local def of sqrt() on VS. With VS, AFAIK the c-library functions are defined in :: if <xxx.h> is used and in :: and std:: if <cxxx> is used (pointing to the same :: function). In this case the local function 'overrides' the :: one which is the one used by std:: - hence std::sqrt() in this case calls the local version.
If you don't want the local version to be used but the std:: (or standard ::) one, then you need to put your function in a namespace. Then :: or std:: will call the standard library version and to use your own, you need to call the one in the namespace.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#include <iostream>
#include <cmath>
//namespace qqq {
double sqrt(double x)
{
std::cout << "Go away, I'm gonna run my own function on " << x << " if I want to! ";
return -999999;
}
//}
int main()
{
std::cout << "The square root of 4 is ..." << std::sqrt(4.0) << '\n';
}
Calls the local one:
The square root of 4 is ...Go away, I'm gonna run my own function on 4 if I want to! -999999
but by using a namespace for the local one, it calls the c-library version:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
#include <iostream>
#include <cmath>
namespace qqq {
double sqrt(double x)
{
std::cout << "Go away, I'm gonna run my own function on " << x << " if I want to! ";
return -999999;
}
}
int main()
{
std::cout << "The square root of 4 is ..." << std::sqrt(4.0) << '\n';
}
I seem to remember this coming up a few times in the past...but what can you do? other than here are some good practices but be careful of the odd little bits of the standard that that may trip you up.
People overloading sqrt/sin also doesn't normally come up in this forum. What you demonstrated is going to be a problem regardless of "using namespace std" when dealing with most C library functions. I'll usually just tell beginners to not put it in a header file; don't care when people use it in individual files.
People overloading sqrt/sin also doesn't normally come up in this forum.
No, the ones that get you are the less commonly used things that you forgot was in the giant namespace.
eg, if you were dealing with your own class that handled currency, and had a member money, and like a good code monkey you put in your getters and setters... oops https://en.cppreference.com/w/cpp/io/manip/get_money
For me VS2019 spits out two errors with the original code:
line 5, "C2169 'sqrt': intrinsic function, cannot be defined"
line 12, "C2264 'sqrt': error in function definition or declaration; function not called"
Remove/comment out the <cmath> header and the errors still barf up. <iostream> includes other headers?
Enclose the custom sqrt function in a custom namespace and VS2019 has no complaint. Even without <cmath> std::sqrt is called.
visual studio is notorious for that. It frequently seems to be able to find things you did not include anywhere, and when you move the code to another compiler the stuff you left off stops working. Its actually better than it was in vs 2008 / vs.net original era and earlier, but they have not completely eradicated the magic.
For me VS2019 spits out two errors with the original code:
line 5, "C2169 'sqrt': intrinsic function, cannot be defined"
line 12, "C2264 'sqrt': error in function definition or declaration; function not called"
hmm, my VS2019 just gives a warning (C28251: Inconsistent annotation for function: this instance has an error) on the original code. 🤷♂️
If a function is an intrinsic, the code for that function is usually inserted inline, avoiding the overhead of a function call and allowing highly efficient machine instructions to be emitted for that function.
Whether it is a standard header or not, it is reasonable to expect that the programmer who #includes a header is expected to be aware of the names (at least the global names) that are (could be) brought in by that header. This does not solve all problems when we switch to a new revision of the standard if it does introduces additional names which are also visible in the global namespace.