5
#include <cmath>

double log(double) {return 1.0;}
int main() {
  log(1.0);
}

Suppose the function log() in <cmath> is declared in global namespace (this is unspecified in fact, and we just make this assumption), then it refers to the same function as the log() function we defined.
So does this code violate the one-definition rule (see here, since no diagnostic required, this code may compile in some compiler and we cannot assert if it is correct)?

Note: After recent edits, this is not a duplicate of: What exactly is One Definition Rule in C++?

xskxzr
  • 12,442
  • 12
  • 37
  • 77
  • 2
    If 2 functions contained with exactly same signatures, are defined differently in the same scope, then the ODR is certainly violated. Some implementers take liberty of Not putting standard stuff into `namespace std`. In those cases, if you define the same name function, then the ODR gets violated. – iammilind Jan 16 '17 at 07:24
  • @SergeBallesta, Upvoted your answer. I would advice to remove the "off topic" part to receive unnecessary negative attention. Closed this Qn, based on the main query it was asking, which looked similar to the other one. Should you feel that, in its current form it's still not answered in that post, then you may reopen it or flag it or let me know. If the answers are collected under a single Qn, then it will be helpful to the community. – iammilind Jan 16 '17 at 07:36
  • @iammilind, I have removed the duplicated part according to the advice from Serge Ballesta. – xskxzr Jan 16 '17 at 07:53
  • 1
    @xskxzr - And now my answer is off topic. Bad SO etiquette. – StoryTeller - Unslander Monica Jan 16 '17 at 08:27

3 Answers3

3

The following addresses a previous revision of the OP. I leave it here in case future readers come here with a similar query.

I guess that two names refer to the same entity if and only if they have the same declarative region, where the concept "declarative region" is defined in the standard [...] Is this guess correct? Is there any word in the standard supporting this?

It's called variable hiding or shadowing colloquially. And the standard says what you said almost verbatim. §3.3.10 ¶1 in the current C++17 standard draft:

A name can be hidden by an explicit declaration of that same name in a nested declarative region or derived class


So does this code violate the one-definition rule (see here, since no diagnostic required, this code may compile in some compiler and we cannot assert it is correct)?

I won't expect it to. The standard requires that all cheader headers (and in particular cmath) introduce their symbols in the std namespace. An implementation that also pours it into the global namespace is standard conforming (since the standard leaves that bit as unspecified), but I would find in bad form. You are correct that it could happen. Now if you were to include math.h (against sage advice) then that would definitely result in a violation of the one definition rule.

StoryTeller - Unslander Monica
  • 165,132
  • 21
  • 377
  • 458
  • *An implementation that also pours it into the global namespace is not standard conforming.* Wrong. 17.6.1.2 Headers [headers] in n4296 draft for C++14 explicitely declares that the inclusion of symbols in global namespace is *unspecified* meaning that implementation are free to do or not: *...In the C++ standard library, however, the declarations (...) are within namespace scope 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.* – Serge Ballesta Jan 16 '17 at 07:07
  • @SergeBallesta - I could be shooting from the hip here, but I think this is a retroactive phrasing to make popular implementations be standard conforming. I seem to recall there was a requirement for stricter separation in C++03. – StoryTeller - Unslander Monica Jan 16 '17 at 07:17
  • @SergeBallesta - Yup. See [here](http://cs.nyu.edu/courses/fall11/CSCI-GA.2110-003/documents/c++2003std.pdf). It was a much stricter *"In the C + + Standard Library, however, the declarations and definitions (except for names which are defined as macros in C) are within namespace scope (3.3.5) of the namespace std.*" – StoryTeller - Unslander Monica Jan 16 '17 at 07:21
  • Your answer is good, & when I was about to upvote, I got little confused by *"So does this code violate the one-definition rule" -- "No..."*. Why do you feel that, the code doesn't violate ODR, when 2 functions with same signature are implemented in the same scope? I suppose, you understand it, but in your answer it's not clear enough. You are right that, a conforming compiler should put all those things into `std`. However if it doesn't put then, ODR is certainly violated. – iammilind Jan 16 '17 at 07:21
  • 1
    @iammilind - Well, rephrased. I didn't think the wording was changed from C++03 to C++14 (see quotes by Serge and myself). – StoryTeller - Unslander Monica Jan 16 '17 at 07:24
  • @StoryTeller: It was not stricter in C++03. Nothing was explicitely forbidding the inclusion of C standard library in global namespace. The addition in C++14 only made explicit that it was not specified at all (and as such **possible**) – Serge Ballesta Jan 16 '17 at 07:35
  • @SergeBallesta - Making it explicit is done retroactively because people did it despite the expectations of the committee. Omitting the allowance originally is the same as forbidding it, IMO. Things are standard conforming if the standard explicitly makes allowance for them, that's true. But if it doesn't make allowance, it is the same as saying this does not make a valid C++ program. – StoryTeller - Unslander Monica Jan 16 '17 at 07:40
  • Hiding has nothing to do with ODR violation. As your quote says, hiding happens if you use the same name in a nested namespace or a derived class. Neither is happening here. – MikeMB Jan 16 '17 at 08:25
  • @MikeMB - Here's a suggestion. Go to the OP, and look at the edit history. How does the answer I wrote **an hour** ago looks now? – StoryTeller - Unslander Monica Jan 16 '17 at 08:28
3

Typical scenario.

If extern "C" double log(double) is initially declared in the global namespace, then you have redeclared it and provided a definition. The implementation's previous mention of extern "C" carries over to your matching redeclaration. Your definition applies to the function belonging to the implementation, and it is an ODR violation.

As for the manifestation of UB: It's apparently common to treat log as a weak linker symbol. Your implementation overrides libc.so according to ABI rules.

(If the implementation doesn't do extern "C", it's still basically all the same.)

Other likely scenario.

If log is declared in namespace std and then brought into the global namespace, then your declaration will conflict with it. (Actually, a using declaration is technically a declaration.) This error is diagnosed.

Assumption-violating scenario.

then it refers to the same function as the log function we defined

One way for the implementation to put <cmath> names into the global namespace would be to declare extern "C" functions inside namespace std, then do using namespace std, and to ensure that this always happens as the first thing when any standard header is included. Since using namespace isn't "sticky" — it only applies to preceding declarations in the nominated namespace — the rest of the standard library would not be made visible. (This would not declare the names in the global namespace, but the standard only says "placed within the global namespace scope.")

In such an implementation, your declaration would hide the standard one and declare a new function with a new mangled name (say _Z3logd instead of simply log) and a new fully-qualified name (::log instead of ::std::log). Then there would be no ODR violation (unless some inline function uses one log in one TU and the other in a different TU).

Potatoswatter
  • 134,909
  • 25
  • 265
  • 421
2

Beware. The ODR only concerns definitions that will be included in the resulting program. That means that is does not concern symbols that could be present in libraries, because a (normal) linker does not load the whole libraries, but only the parts that are required to resolve symbols. For example in this code:

#include <cmath>

double log(double) {return 1.0;}

int main()
{
    log(1.0);
}

There is no violation of the ODR:

  • either the log symbol from the C standard library was only included in the std namespace and there is no collision at all
  • or it is also included in global namespace

In latter case, the declaration double log(double) does not conflict with the one from cmath because it is the same. And as the symbol log is already defined, its definition from the standard library will not be included in the program. As such, only one definition for the log function exists in the program, that one: double log(double) {return 1.0;}.

Things would be different if you extracted the object module containing log from the math library and explicitely link it in your program. Because object modules are always included in the resulting program whereas object modules in libraries are only conditionaly included if they resolve undefined symbols.


References from standard:

Draft n3337 for C++11 or n4296 for C++14 (or n4618 for last revision) are explicit in paragraph 2.2 Phases of translation [lex.phases]:

§9. All external entity references are resolved. Library components are linked to satisfy external references to entities not defined in the current translation. All such translator output is collected into a program image which contains information needed for execution in its execution environment.

As shown code uses only one translation unit and as log is already defined in it, the definition from the library will not be used.

Serge Ballesta
  • 143,923
  • 11
  • 122
  • 252
  • This answer was originally posted [there](http://stackoverflow.com/a/41671382/3545273)... – Serge Ballesta Jan 16 '17 at 08:50
  • "linked to satisfy external references to functions and objects not defined in the current translation" does not imply that symbols in the translation with `main` must override library symbols. Overriding is only defined for `operator new`. "The current translation" includes libraries (such as `libc.so` on Unix which the compiler driver implicitly links by default), per the preceding bullet in [lex.phases]. – Potatoswatter Jan 16 '17 at 10:07
  • The quoted text from C may be found at [lex.phases] §2.2 in the latest draft N4618. C and C++ have somewhat different compilation models. – Potatoswatter Jan 16 '17 at 10:10
  • @Potatoswatter: Thank you very much! I could not find it and it is in fact present at least since C++. I'l edit my answer with it... – Serge Ballesta Jan 16 '17 at 12:31