仅在一个编译单元中使用的类型如何违反单一定义规则?

时间:2010-08-18 14:59:49

标签: c++

有人告诉我,这些在自己独特的翻译单元中可见的类型违反了“一个定义规则”。有人可以解释一下吗?

//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;
} 

我知道如何解决这个问题。根据标题,我一直在寻找为什么它是ODR违规。它是如何违反的:“在任何翻译单元中,模板,类型,功能或对象只能有一个定义。”?或者它可能违反规则的不同部分。

3 个答案:

答案 0 :(得分:11)

问题是,即使s1s2只有内部链接,S的两个相应定义都有外部链接。

您要做的是使用匿名命名空间:

//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(); }

编辑:

匿名命名空间内的所有内容,包括类定义,都有内部链接。

匿名命名空间中的定义仍然具有外部链接,但编译器确保它们接收不会与其他翻译单元中的任何定义冲突的唯一名称。

答案 1 :(得分:10)

这是不安全的,因为您有两个名为S的结构。 static关键字仅适用于变量声明;这相当于你写的:

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

static S s1;

编译器在编译时没有注意到这一点,因为它分别处理每个翻译单元。结构中的Value函数被修改为完全相同的名称,并成为目标文件中的弱全局符号,因此链接器不会引发有关符号名称冲突的错误;它只选择一个用于完全链接的二进制文件。这可能是第一个符号定义,这意味着您实际上可以根据链接对象的顺序获得不同的行为:

> 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

你可以通过在匿名命名空间中包装结构来解决这个问题(这会使Value函数符号本地而不是弱全局变量):

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

或者只是删除结构的名称,因为您实际上并不需要它:

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

答案 2 :(得分:7)

您已经以两种不同的方式在全局命名空间中定义了struct S,从而打破了One Definition Rule。特别是,::S::Value()有两种不同的定义,它是未定义的,实际上最终会被调用。

您应该使用无名称空间来确保在每个翻译单元中定义明确命名的struct S版本:

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

一个定义规则比你引用的第一个段落要多得多。最后一段基本上说有些东西,包括类定义,可以在程序中出现多次,只要它们都是相同的。您的代码打破了最后一个条件。或者,在标准的(删节)单词中:

  

在程序中可以存在多个类类型的定义...如果每个定义出现在不同的翻译单元中,并且定义满足以下要求。鉴于这样一个名为D的实体在多个翻译单元中定义,则D的每个定义应由相同的标记序列组成。