Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can a type that is used only in one compilation unit, violate the One Definition Rule?

Tags:

c++

I was told that these types, that are visible in there own unique translation unit, were in violation of the One Definition Rule. Can someone explain this?

//File1.cpp
#include "StdAfx.h"
static struct S { int Value() { return 1; } } s1;
int GetValue1() { return s1.Value(); }

//File2.cpp
#include "StdAfx.h"
static struct S { int Value() { return 2; } } s2;
int GetValue2() { return s2.Value(); }

// main.cpp
#include "stdafx.h"
extern int GetValue1();
extern int GetValue2();
int _tmain(int argc, _TCHAR* argv[])
{
    if( GetValue1() != 1 ) throw "ODR violation";
    if( GetValue2() != 2 ) throw "ODR violation";
    return 0;
} 

I know how to fix the problem. As per the title, I was looking to why it was a ODR violation. How does it violate: "In any translation unit, a template, type, function, or object can have no more than one definition."? Or maybe it violates a different part of the rule.

like image 474
jyoung Avatar asked Aug 18 '10 14:08

jyoung


People also ask

Which one is the correct description of the one definition rule?

The One Definition Rule (ODR) is an important rule of the C++ programming language that prescribes that objects and non-inline functions cannot have more than one definition in the entire program and template and types cannot have more than one definition by translation unit.

What does ODR used mean?

10. +1 for the succinct opening sentence: "In plain word, odr-used means something(variable or function) is used in a context where the definition of it must be present."


3 Answers

The problem is that, even though s1 and s2 have only internal linkage, both of the corresponding definitions of S have external linkage.

What you want to do is to use an anonymous namespace:

//File1.cpp
#include "StdAfx.h"
namespace {
    struct S { int Value() { return 1; } } s1;
}
int GetValue1() { return s1.Value(); }

//File2.cpp
#include "StdAfx.h"
namespace {
    struct S { int Value() { return 2; } } s2;
}
int GetValue2() { return s2.Value(); }

Edit:

Everything inside the anonymous namespace, including the class definitions, has internal linkage.

Definitions in the anonymous namespace still have external linkage, but the compiler ensures that they receive unique names that won't clash with any definitions from other translation units.

like image 187
Martin B Avatar answered Nov 16 '22 01:11

Martin B


This is unsafe because you have two structs named S. The static keyword only applies to the variable declaration; it's equivalent to you having written:

struct S {
    int Value() {return 1;}
};

static S s1;

The compiler doesn't notice this at compile time because it deals with each translation unit separately. The Value functions in the structs are mangled to exactly the same name, and become weak global symbols in the object files, so the linker doesn't throw an error about a symbol name collision; it just picks one to use in the fully-linked binary. This will probably be the first symbol definition, which means you can actually get different behavior depending on the order you linked the objects:

> g++ -o test test.o test1.o test2.o && ./test
s1 is 1
s2 is 1

> g++ -o test test.o test2.o test1.o && ./test
s1 is 2
s2 is 2

You can get around this by either wrapping the structs in anonymous namespaces (which will make the Value function symbols locals instead of weak globals):

namespace {
    struct S {
        int Value() {return 1;}
    } s1;
}

Or simply removing the name of the struct, since you don't actually need it:

struct {
    int Value() {return 1;}
} s1;
like image 30
Michael Mrozek Avatar answered Nov 16 '22 00:11

Michael Mrozek


You have defined struct S in the global namespace in two different ways, which breaks the One Definition Rule. In particular, there are two different definitions of ::S::Value(), and it's undefined which will actually end up being called.

You should use nameless namespaces to make sure a distinctly named version of struct S is defined in each translation unit:

namespace { struct S {int Value() {return 1;}} s1; }
int GetValue1() {return s1.Value();}

There's a lot more to the One Definition Rule than the first paragraph which you quote. The last paragraph basically says that some things, including class definitions, can appear more than once in a program, as long as they are all identical. Your code breaks this last condition. Or, in the (abridged) words of the Standard:

There can be more than one definition of a class type ... in a program provided that each definition appears in a different translation unit, and provided the definitions satisfy the following requirements. Given such an entity named D defined in more than one translation unit, then each definition of D shall consist of the same sequence of tokens.

like image 37
Mike Seymour Avatar answered Nov 16 '22 02:11

Mike Seymour