71

In the code below, I define a trivial log function. In main I try not to call it; I call std::log. Nevertheless, my own log is called; and I see "log!" on screen. Does anyone know why? I use G++ 4.7 and clang++ 3.2.

#include <iostream>
#include <cmath>

double log(const double x) { std::cout << "log!\n"; return x; }

int main(int argc, char *argv[])
{
  std::log(3.14);
  return 0;
}
user2023370
  • 10,488
  • 6
  • 50
  • 83
  • 12
    sounds like a serious compiler bug... – MFH Aug 09 '12 at 22:38
  • I can reproduce this on g++ 4.6 under Macports. It does not happen in g++ 4.2 or 4.4, though. – carlosdc Aug 09 '12 at 22:39
  • 1
    http://codepad.org/Uwhgrv7q http://codepad.org/z07Ctfyn Frome these two i would say that the std::log() function calls log() . but then it should generate an error/warning that your file redefines log or something like t – Gir Aug 09 '12 at 22:42
  • 2
    This is interesting. I can reproduce this even on ideone... http://ideone.com/wSPVF I'd like to know the answer to this myself. – Constantinius Aug 09 '12 at 22:42
  • i tried the same in ideone - if you comment out the new log function it calls the mathematic log function – Gir Aug 09 '12 at 22:45
  • 5
    @ColeJohnson: It's why you use *namespaces*. Not everything needs to be in a class. Personally, I put everything in a namespace except `main`. – GManNickG Aug 10 '12 at 01:03

5 Answers5

59

C++ Standard 17.6.1.2 paragraph 4 (emphasis mine):

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

g++ does it the latter way so that some of the same header files can be reused for C and C++. So g++ is allowed to declare and define double log(double) in the global namespace.

Section 17.6.4.3.3 paragraphs 3 and 4:

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.

Each function signature from the Standard C 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.

And up at the top of Section 17.6.4.3 paragraph 2:

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.

You, on the other hand, may not declare or define ::log in any way.

It's too bad the g++ toolchain doesn't give you any error messages, though.

aschepler
  • 70,891
  • 9
  • 107
  • 161
  • By "C++ Standard" I presume you mean the C++11 standard (N3337); the symbolic name for 17.6.1.2 is `[headers]`, and 17.6.4.3 is `[reserved.names]` – M.M Mar 21 '18 at 04:11
9

What happens, I expect, is that std::log simply delegates to ::log. Unfortunately, ::log only provides a float overload, and you kindly provide a double overload, making yours a better match. But I still don't see how it even gets considered in the overload set.

Puppy
  • 144,682
  • 38
  • 256
  • 465
  • `` is required to provide `log(double)`. – aschepler Aug 09 '12 at 22:48
  • 1
    I expected the same on MSVC10 and I _eventually_ did get a duplicate symbol error on link but only after 3 or 4 rebuilds. – Captain Obvlious Aug 09 '12 at 22:49
  • @aschepler: `` is required to provide `double std::log(double)` (I'm making the focus on namespace a big deal, because it is right now). The standard requires them to be in the `::std` namespace, and each implementation is free to define them in the global namespace if they so choose. – Cornstalks Aug 09 '12 at 22:59
  • 5
    @DeadMG: Don't you mean `::log` (the C library function) provides a `double` parameter type? It's `::logf` that provides the `float` type. – Cornstalks Aug 09 '12 at 23:01
9

On libstdc++'s cmath you will see this:

using ::log;

So it's bringing in the math.h functions from the global namespace into std. Unfortunately you are supplying an implementation for double log(double), so the linker will not use the one from the math lib. So definitely a bug in libstdc++.

EDIT: I claim it's a bug in libstdc++ because std::log should not suffer from interferences with the C library when you are explicitly asking for the std:: versions. Of course, this way to override standard library functions is an old "feature" from the C language.

EDIT 2: I found out that the standard doesn't actually forbid bringing names from the global namespace into std. So not a bug after all, only a consequence of the implementation details.

DanielKO
  • 4,422
  • 19
  • 29
  • The `using` declaration should only import the declarations that are visible at that point, I think, which would mean that a later declared function should not get imported this way. – bames53 Aug 09 '12 at 22:55
  • I edited my answer, the standard allows for this kind of implementation, that first declares the math.h functions, then brings the `double` versions into `std`. – DanielKO Aug 09 '12 at 23:09
6

In C++, the compiler is free to implement the C library in the global namespace and delegate to it (this is implementation defined).

17.6.1.2.4 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).

In general, I'd avoid making a function with the same signature as one of the C standard library's. The C++ standard certainly gives compilers the freedom to be using these signatures if it so chooses, which means you may be fighting your compiler if you try to use the same signatures. Hence, you get weird results.

I would expect a linker error or warning though, and I think it may be worth reporting this.

[edit]

Wow, ninja'd.

Cornstalks
  • 37,137
  • 18
  • 79
  • 144
0

Because you've overridden it in the global namespace. Using a namespace avoids that danger if you don't want to move on to a safer, cleaner language like Nim for example.

Proper use of namespace demo:

#include <iostream>
#include <cmath>  // Uses ::log, which would be the log() here if it were not in a namespace, see http://stackoverflow.com/questions/11892976/why-is-my-log-in-the-std-namespace

// Silently overrides std::log
//double log(double d) { return 420; }

namespace uniquename {
    using namespace std;  // So we don't have to waste space on std:: when not needed.

    double log(double d) {
        return 42;
    }

    int main() {
        cout << "Our log: " << log(4.2) << endl;
        cout << "Standard log: " << std::log(4.2);
        return 0;
    }
}

// Global wrapper for our contained code.
int main() {
    return uniquename::main();
}

Output:

Our log: 42
Standard log: 1.43508
Cees Timmerman
  • 17,623
  • 11
  • 91
  • 124