Question

According to the c++ standard:

No translation unit shall contain more than one definition of any variable, function, class type, enumeration type, or template.

//--translation_unit.cpp--//
int a;

void foo()
{
    int a; //Second defention of a. ODR fails.
}

Can you explain me how ODR does work actually?

Was it helpful?

Solution

This doesn't break the rule because you define two different variables. They have the same name, but are declared in different scopes, and so are separate entities. Each has a single definition.

The declaration in the function's scope is said to hide the one in the global namespace. Within the function, the unqualified name a refers to the local variable, while the qualified name ::a refers to the global.

OTHER TIPS

They do not violate the ODR because they have different scope.

The first a has global scope

A variable that has global scope (also termed file scope) is known throughout the file after the point where it is defined

The second a has local scope

A variable that has local scope (also termed block scope) is known only within the block in which it is defined

For understand more clear about ODR of C++ the concepts you should investigate are: Storage Duration, Scope, and Linkage

You did not define a again.

You just defined a new variable a. It has the scope only inside the function, and has nothing to do with the original one(which has a global scope), and shadowed the original one inside the function.

Can you explain me how ODR does work actually?

Here is an example of violation of ODR:

/* file : module.cpp */
#include <stdio.h>
inline int foo() {
    printf("module.foo: %p\n", &foo);
    return 1;
}        
static int bar = foo();

/* file : main.cpp */
#include <stdio.h>
inline int foo() {
    printf("main.foo: %p\n", &foo);
    return 2;
}
int main(int argc, char *argv[]) {
    return foo();
}

As you can see, function int foo() defined differently in two modules. Now observe, how it produces different behavior depending on requested optimization level (O3 vs O0):

$ clang++ --std=c++11 -O0 main.cpp module.cpp && ./a.out 
module.foo: 0x100a4aef0
module.foo: 0x100a4aef0

$ clang++ --std=c++11 -O3 main.cpp module.cpp && ./a.out 
module.foo: 0x101146ee0
main.foo: 0x101146ee0

Output is different, because for inline functions compiler produces a linker symbol in each compilation module. This symbol (in each compilation module) is marked as "pick any, they are all the same". In the first case, when all optimizations are disabled, linker picks up definition from module.cpp. In the second case, compiler just inlines both functions so no additional work from linker is required.

There are other examples when violation of ODR produces weird behavior. So, don't do it :)

P.S. Bonus, case from real life:

/* a.cpp */
struct Rect
{
    int x,y,w,h;
    Rect(): x(0),y(0),w(0),h(0)
};
/* b.cpp */
struct Rect
{
    double x,y,w,h;
    Rect(): x(0),y(0),w(0),h(0)
};

The problem here is the same as in previous example (because Rect constructors are implicitly inline). Depending on phase of the moon compiler was picking one implementation or another producing strange results (int version will leave part of doubles uninitialized, double version will go outside of ints and corrupt memory there). Good way to protect from that is to use anonymous namespaces (C++) or declare struct as static (C):

/* file.cpp */
namespace {
    struct Rect { ... };
}
/* file.c */
static struct Rect { ... };
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top