Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

No ambiguous reference error even after using namespace directive

The following code generates call of overloaded ‘bar()’ is ambiguous error which it should be as I have a function bar in both global and foo namespace and I have called using namespace foo directive.

namespace foo {
    void bar() {}
}

void bar() {}

using namespace foo;

int main() {
    bar();
}

I was expecting the same error with the following code too:

#include <cstdlib>
#include <iostream>

int abs(int n) {
    return n > 0 ? n : -n;
}

using namespace std;

int main() {
    int k;

    cin >> k;

    cout << abs(k) << endl;
}

I have defined a function int abs(int n) like the one present in cstlib and I have called using namespace std directive. So there should have been an error like the first example. But there is none.

My question is how the compiler is resolving this ambiguity? Which function will be called in such cases, mine or std's one? Is there any UB involved here?

Update: From comments and answers it seems that different compilers are behaving differently. So is this behavior undefined or implementation defined?

I have tested it with g++ 4.8.4 on Ubuntu 14.04 with -std=c++11 flag.

[Please note that I do understand that using namespace std is bad and my abs function is no better or even worse than std one. My question is different.]

like image 956
taskinoor Avatar asked Apr 06 '17 19:04

taskinoor


1 Answers

In the C++ standard section 17.6.1 Library contents and organization, we read in 17.6.1.2:

Except as noted in Clauses 18 through 30 and Annex D, the contents of each header cname shall be the same as that of the corresponding header name.h , as specified in the C standard library (1.2) or the C Unicode TR, as appropriate, as if by inclusion. In the C ++ standard library, however, the declarations (except for names which are defined as macros in C) are within namespace scope (3.3.6) of the namespace std. 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 (7.3.3).

emphasis added

Additionally, in 17.6.4.3.2 External linkage we read

Each name from the Standard C library declared with external linkage is reserved to the implementation for use as a name with extern "C" linkage, both in namespace std and in the global namespace

In plain English from this section and similar, C standard library names are reserved, but C standard library names are only in the global namespace scope.

What GLIBCXX is doing here is perfectly valid; it's declaring an abs in the global namespace scope and injecting it into std using using-declarations.

Indeed, in the standard library that my system / g++ 4.8.5 and 6.3.0 use (6.3.0 I checked on coliru), <cstdlib> looks something like this:

// <stdlib.h>:

extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;
// <cstdlib>

#include <stdlib.h>

namespace std
{
    using ::abs;
}

It's that using ::abs which makes std::abs call your function.

You violate the ODR because the GLIBC is a shared library and it also provides an implementation for int abs(int).

That you don't get a "multiple definition of abs(int)" linker error is arguably a bug in the compilers; it would be nice if they warned as about this undefined behavior.


This can be reproduced with this example:

main.cpp

#include <iostream>

int myabs(int);

namespace foo {
    int myabs(int n) {
        return ::myabs(n);
    }
}

int myabs(int n) {
    std::cout << "myabs inside main.cpp\n";
    return n > 0 ? n : -n;
}

using namespace foo;

int main() {
    int k = -1;

    std::cout << foo::myabs(k) << std::endl;
}

myabs.cpp

#include <iostream>

int myabs(int n) {
    std::cout << "myabs inside myabs.cpp\n";
    return n > 0 ? n : -n;
}

Then on the commandline:

g++ -fPIC -c myabs.cpp
g++ -shared myabs.o -o libmyabs.so
g++ -L. main.cpp -lmyabs

Running ./a.out calls the myabs defined inside main.cpp, whereas if you comment out the myabs in main.cpp, it calls the one from myabs.cpp


How to avoid this problem

If you avoid declaring functions in the global namespace, you should mostly avoid this problem.

For your example, if we instead write:

#include <cstdlib>
#include <iostream>

namespace {
    int abs(int n) {
        return n > 0 ? n : -n;
    }
}

using namespace std;

int main() {
    int k;

    cin >> k;

    cout << abs(k) << endl;
}

We get the expected error warning about the call being ambiguous. However, be warned that this doesn't solve the problem if the standard library declares abs in the global namespace:

int main() {
    int k;

    cin >> k;

    cout << ::abs(k) << endl;
}

That seems to just call the standard library version. Naturally, this problem can be avoided by avoiding using namespace std

like image 150
Justin Avatar answered Oct 13 '22 00:10

Justin