Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/128.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 一个仅在一个编译单元中使用的类型如何违反一个定义规则?_C++ - Fatal编程技术网

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

C++ 一个仅在一个编译单元中使用的类型如何违反一个定义规则?,c++,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;

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

//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。它是如何违反“在任何翻译单元中,模板、类型、函数或对象只能有一个定义”的?或者它违反了规则的不同部分。

问题是,尽管
s1
s2
只有内部链接,但
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(); }
编辑:

匿名命名空间中的所有内容(包括类定义)都具有内部链接


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

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

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

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

> 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

您可以通过将结构包装到匿名名称空间(这将使
函数符号成为局部符号,而不是弱全局符号)来解决此问题:


或者简单地删除结构的名称,因为实际上不需要它:

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

您在全局命名空间中以两种不同的方式定义了
struct S
,这打破了一个定义规则。特别是,
有两种不同的定义::S::Value()
,它是未定义的,实际上会被调用

您应该使用无名名称空间,以确保在每个翻译单元中定义了一个命名明确的
struct S

namespace { struct S {int Value() {return 1;}} s1; }
int GetValue1() {return s1.Value();}
一个定义规则比你引用的第一段要多得多。最后一段基本上说,一些东西,包括类定义,可以在程序中出现多次,只要它们都相同。你的代码破坏了最后一个条件。或者,用本标准的(节略)文字:

一个类类型可以有多个定义。。。在程序中,如果每个定义出现在不同的翻译单元中,并且定义满足以下要求。给定在多个翻译单元中定义的名为D的实体,则D的每个定义应包含相同的标记序列


这根本不是真的。在一个未命名的名称空间中对任何链接都没有影响。我知道如何解决这个问题。我想知道为什么这是违法行为。它是如何违反“在任何翻译单元中,模板、类型、函数或对象只能有一个定义”的?或者它违反了规则的另一部分。未命名的名称空间不影响链接,它只是将内容放在一个不同于其他名称空间的名称空间中。他们还有外部联系。@Charles和@Mike:谢谢——我不知道。我会相应地更新答案。@jyoung:这是您违反的ODR中的一点:“有些东西,如类型、模板和外部内联函数,可以在多个翻译单元中定义。对于给定的实体,每个定义必须相同。”从技术上讲,允许有两个::s::Value()的定义因为两者都(隐式)声明为内联(
inline
函数可以在多个TU中定义,并且必须在使用它们的每个TU中定义)。不允许的是定义不同。“只需删除结构的名称…”我不知道在声明结构时可以不命名它。我认为这在class上也是可能的?@Stephane在
class
上也是可能的,但这种用法非常罕见。命名一个结构本质上就是对其进行类型定义,以便以后可以使用该名称作为一个整体引用该结构,但每次都可以继续重新定义匿名结构。例如:
struct{int x;}foo(){struct{int x;}rtn;rtn.x=4;return rtn;}
将声明一个返回结构的函数
foo
,您可以使用
struct{int x;}var=foo()调用它也许使用非常罕见,但现在我知道这是可能的。以Thanx为例,你违反了规则的另一部分。我已经用细节更新了我的答案。
namespace { struct S {int Value() {return 1;}} s1; }
int GetValue1() {return s1.Value();}