为什么结构声明违反C ++中的ODR?

时间:2019-06-02 21:48:21

标签: c++ c++11 header-files one-definition-rule

我试图让编译器对某些我认为不违反C ++中一定义规则的代码作出反应。在头文件中,我有两个声明:一个声明用于结构,一个声明,如下所示:

struct TestStruct {
    int a;
    double d;
};
int k();

然后我故意将头文件两次放入另一个包含main()的文件中,以查看会发生什么。

令我惊讶的是,编译器抱怨该结构的多个定义。我希望编译器根本不会出现任何多重性错误,因为struct和function都具有纯声明。

只有在将结构放入标头保护程序之后,编译器才停止抱怨。但是,没有为该结构分配内存。这不是一个定义。那为什么编译器会发疯呢?

2 个答案:

答案 0 :(得分:1)

在一个翻译单元中最多只能定义一个结构 。

您可以用几种翻译单位进行定义,但是定义必须相同。 (来源:cppreference/ODR)。

为避免此问题,您需要在标头中包含一个包含保护。它将默默地防止在每个翻译单元中多次包含标头。

答案 1 :(得分:0)

只使用一次包含保护(或编译器可用)的编译指示。

#ifndef PATH_TO_FILE_FILENAME_H
#define PATH_TO_FILE_FILENAME_H
struct TestStruct {
    int a;
    double d;
};
int k();
#endif

或(如果可以的话,更好!)

#pragma once
struct TestStruct {
    int a;
    double d;
};
int k();

也许还值得使用命名空间来避免污染全局命名空间

#pragma once
namespace Test
{
    struct TestStruct {
        int a;
        double d;
    };
    int k();
};

请注意,要避免muldef,您还需要在内联中声明k(),以决定在标头中提供其定义(有时,当您需要使用模板而不指定显式模板参数时,这是不可避免的)。 / p>

#pragma once
namespace Test
{
    struct TestStruct {
        int a;
        double d;
    };
    template<typename T>
    inline int k<T>() // This now has to be inline or static.
    {
        // Some implementation
    }
};

编辑:顺便说一句,结构体/类的声明和定义之间的区别与函数没有太大区别:

void TestFunction(); // The compiler now knows there's a function called TestFunctionand can attempt to link the symbol information to its implementation somewhere in the compilation unit.

在这种情况下,我们并没有实现函数的实质,只是说它存在,并且由于编译器知道签名(或函数承诺采取并返回的内容),因此可以快乐地继续。在TestStructs的情况下,向前声明(不执行)将为

class TestStruct;