Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a declaration affect the std namespace?

Tags:

c++

std

reserved

#include <iostream> #include <cmath>  /* Intentionally incorrect abs() which seems to override std::abs() */ int abs(int a) {     return a > 0? -a : a; }  int main() {     int a = abs(-5);     int b = std::abs(-5);     std::cout<< a << std::endl << b << std::endl;     return 0; } 

I expected that the output will be -5and 5, but the output is the -5 and -5.

I wonder why this case will happen?

Does it have anything to do with the use of std or what?

like image 307
Peter Avatar asked Jun 17 '18 16:06

Peter


People also ask

What can be declared in a namespace?

A namespace is a declarative region that provides a scope to the identifiers (names of functions, variables or other user-defined data types) inside it. Multiple namespace blocks with the same name are allowed. All declarations within those blocks are declared in the named scope.

What is wrong with using namespace std?

While this practice is okay for example code, pulling in the entire std namespace into the global namespace is not good as it defeats the purpose of namespaces and can lead to name collisions. This situation is called namespace pollution.

Why do you need to declare using namespace std before the main function?

So when we run a program to print something, “using namespace std” says if you find something that is not declared in the current scope go and check std. using namespace std; are used. It is because computer needs to know the code for the cout, cin functionalities and it needs to know which namespace they are defined.

Does using namespace std affect performance?

It doesn't affect the runtime performance at all.


2 Answers

The language specification allows implementations to implement <cmath> by declaring (and defining) the standard functions in global namespace and then bringing them into namespace std by means of using-declarations. It is unspecified whether this approach is used

20.5.1.2 Headers
4 [...] In the C++ standard library, however, the declarations (except for names which are defined as macros in C) are within namespace scope (6.3.6) of the namespace std. It is unspecified whether these names (including any overloads added in Clauses 21 through 33 and Annex D) are first declared within the global namespace scope and are then injected into namespace std by explicit using-declarations (10.3.3).

Apparently, you are dealing with one of implementations that decided to follow this approach (e.g. GCC). I.e. your implementation provides ::abs, while std::abs simply "refers" to ::abs.

One question that remains in this case is why in addition to the standard ::abs you were able to declare your own ::abs, i.e. why there's no multiple definition error. This might be caused by another feature provided by some implementations (e.g. GCC): they declare standard functions as so called weak symbols, thus allowing you to "replace" them with your own definitions.

These two factors together create the effect you observe: weak-symbol replacement of ::abs also results in replacement of std::abs. How well this agrees with the language standard is a different story... In any case, don't rely on this behavior - it is not guaranteed by the language.

In GCC this behavior can be reproduced by the following minimalistic example. One source file

#include <iostream>  void foo() __attribute__((weak)); void foo() { std::cout << "Hello!" << std::endl; } 

Another source file

#include <iostream>  void foo(); namespace N { using ::foo; }  void foo() { std::cout << "Goodbye!" << std::endl; }  int main() {   foo();   N::foo(); } 

In this case you will also observe that the new definition of ::foo ("Goodbye!") in the second source file also affects the behavior of N::foo. Both calls will output "Goodbye!". And if you remove the definition of ::foo from the second source file, both calls will dispatch to the "original" definition of ::foo and output "Hello!".


The permission given by the above 20.5.1.2/4 is there to simplify implementation of <cmath>. Implementations are allowed to simply include C-style <math.h>, then redeclare the functions in std and add some C++-specific additions and tweaks. If the above explanation properly describes the inner mechanics of the issue, then a major part of it depends on replaceability of weak symbols for C-style versions of the functions.

Note that if we simply globally replace int with double in the above program, the code (under GCC) will behave "as expected" - it will output -5 5. This happens because C standard library does not have abs(double) function. By declaring our own abs(double), we do not replace anything.

But if after switching from int with double we also switch from abs to fabs, the original weird behavior will reappear in its full glory (output -5 -5).

This is consistent with the above explanation.

like image 55
AnT Avatar answered Sep 29 '22 21:09

AnT


Your code causes undefined behaviour.

C++17 [extern.names]/4:

Each function signature from the C standard library declared with external linkage is reserved to the implementation for use as a function signature with both extern "C" and extern "C++" linkage, or as a name of namespace scope in the global namespace.

So you cannot make a function with the same prototype as the Standard C library function int abs(int);. Regardless of which headers you actually include or whether those headers also put C library names into the global namespace.

However, it would be allowed to overload abs if you provide different parameter types.

like image 45
M.M Avatar answered Sep 29 '22 22:09

M.M