10

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

14
  • It's worth noting that it calls your abs function, not the one in std. At least in g++ and clang Commented Apr 6, 2017 at 19:23
  • what compiler and with what switches you do you. Your code does generate eror with C++11 compliance Commented Apr 6, 2017 at 19:26
  • @Justin I also suspected that in g++ but I'm not sure whether it is a defined or undefined or implementation defined behavior. Commented Apr 6, 2017 at 19:26
  • 2
    In VS2015, if I try cout << std::abs(k) << endl it still calls the user defined version. Commented Apr 6, 2017 at 19:32
  • 1
    VS2015 cmath contains essentially std::namespace{ using ::abs; using ::acos; using ::asin; /*etc...*/ }. Commented Apr 6, 2017 at 19:43

5 Answers 5

6

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

Sign up to request clarification or add additional context in comments.

2 Comments

There is no definition of abs here, did you mean "The weird thing is that we don't get a 'multiple definition of myabs' error here"?
Maybe it's relying on a compiler built-in to provide abs, and defining our own global abs function takes priority over the built-in without counting as a multiple definition error.
5

The problem is that <cstdlib> is really complicated due to the interactions between the C headers and the C++ headers. In libstdc++, it's not implemented as:

namespace std {
    int abs(int );
}

If that were the case, then your sample program with std::abs would match your expectation about your sample program with foo::bar, for precisely the same reasons. But instead, it's declared as something like:

// from <stdlib.h>
extern int abs(int );

// from <cstdlib>
#include <stdlib.h>

namespace std {
    using ::abs;
}

When you declared and defined your own ::abs(int ), that is simply a redeclaration of the previously declared int ::abs(int ). You're not overloading anything - there is just one int ::abs(int) in this translation unit! You could see that if you tried to declare something like long abs(int ) - you'd get an error about redeclaration with a different return type.

This works because ::abs in the C header isn't defined (otherwise you'd get a compile error on a redefinition) - you bring that definition in through the shared library. And so you end up with an ODR violation because you have your definition in the TU and the shared library definition in GLIBC, and hence, undefined behavior. I'm not sure why the linker doesn't catch it.

3 Comments

standard covered this issue by reserving library names
ODR violations are mostly No Diagnostic Required, so the linker isn't required to report anything. I think common implementations allow you to 'replace' standard library functions, e.g. so you can write your own malloc.
@M.M possible and before relaxation of C++11's ODR. ODR relates to similar definitions that would cause ambiguity, while C++ headers may define library function differently depending on platform-specific issues
1

If abs function is declared in following way:

void abs(int n) { return n > 0 ? n : -n; } (return type is changed from int to void)

this will raise error: ambiguating new declaration of 'void abs(int)'

Because in stdlib it it declared as int abs(int n) but we're defining it now with another return type.

So why it is not complaining when I defining it with correct return type?

First of all, implementation of int abs(int k) resides in compiled form (standard library) not in source form. So it is not possible to to tell (before linking) if any int abs(int k) is already defined or not. So compiler is happy with declaration in cstdlib and definition in our provided source. And when it starts linking it only search for function's which is declared but not defined yet(so that it can copy the definition (assumed linking against a static library)). So linker won't search for another definition of int abs(int k). Finally our given definition is included in resulting binary.

Comments

-1

I've noticed the following inside <cstdlib>:

#ifndef __CORRECT_ISO_CPP_STDLIB_H_PROTO
  inline long
  abs(long __i) { return __builtin_labs(__i); }
  //...

When I try your example using long,

#include <cstdlib>
#include <iostream>

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

using namespace std;

int main() {
    long k;

    cin >> k;

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

I get the expected error:

error: call of overloaded 'abs(long int&)' is ambiguous

Maybe your implementation is doing something similar.

5 Comments

Same error: wandbox.org/permlink/QxYVD0rWlNldCFFT . Also, there is a definition for int as well (In this version on wandbox: extern int abs (int __x) __THROW __attribute__ ((__const__)) __wur;)
@Justin For ambiguity, k should be long also.
Yeah, oops. The expected error occurs when k is long
But why there isn't one with int version? cstdlib clearly has an int version.
long labs(long) has the same behavior as int abs(int); regarding this issue.
-1

Let's modify this code to this:

#include <iostream>
#include <cstdlib>

int abs(int n) {
    std::cout << "default abs\n";
    return n > 0 ? n : -n;
}

//using namespace std;

int main() {
    int k;

    std::cin >> k;

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

It STILL will call your abs. Strange , huh? Ok, actually there is no int abs(int) function in std namespace. There is no ambiguous call here, depending on used platform, because actual abs defined as equal to this:

std::intmax_t abs( std::intmax_t n );

But actual implementation may vary, depending on a number of factors. What you've did is that you had either had overload the function or a template. As long as you won't hit the exact definition in header file, your function will be used if it matches better to arguments. It may be tried as candidate by std templates instead of std::abs() function if std namespace is used globally. That's one of caveats behind using namespace std in global scope.

in fact, on my system std::abs defined as an abs from global scope: Of course, you have a function from global scope with such prototype, defined by yourself, so std::abs call in my case is equal to ::abs call.

#include <iostream>
#include <cstdlib>

int abs( long n ) {
    std::cout << "default abs\n";
    return n > 0 ? n : -n;
}

//using namespace std;

int main() {
    int k;

    std::cin >> k;

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

Now it uses standard library function and outputs absolute value of k.

Let's see what cstdlib header contains in particular case:

_STD_BEGIN
using _CSTD size_t; using _CSTD div_t; using _CSTD ldiv_t;   
using _CSTD abort; using _CSTD abs; using _CSTD atexit;
// and so on..
_STD_END

_STD_BEGIN defined as

#define _STD_BEGIN  namespace std {

Effectively we have

namespace std {
     using ::abs;
}

This way anything that got identifier abs in global scope becomes std::abs This got force of forward declaration, so abs() defined after this definition is the subject. Because language syntax allows that, redefining library identifiers in global scope might result in ill-formed program or to UB, which in this case comes down to which declarations are active in header.

The C++ standard library reserves the following kinds of names:

  • macros
  • global names
  • names with external linkage

If a program declares or defines a name in a context where it is reserved, other than as explicitly allowed by this Clause, its behavior is undefined.

13 Comments

The standard library contains int std::abs(int). It's not all just promoted to intmax_t.
"actually there is no int abs(int) function in std namespace" - I don't know about what it's like on your system, but there's supposed to be a version that takes and returns an int.
@user2357112 or it is implicitly casted to int? in fact, on my system std:;abs defined as an abs from global scope
If there is no int abs(int) in std then isn't it violating the documents as the documentation is clear about it?
@Swift The question is, if int std::abs(int) is just int ::abs(int) being pulled into namespace std, then the why doesn't the user defined int ::abs(int) conflict with the existing int ::abs(int)? If there is no existing int ::abs(int) then it couldn't be pulled into namespace std.
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.