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

Why does a struct declaration violate the ODR in C++?

我试图让编译器对一些我认为不违反 C++ 中的 one-definition-rule 的代码做出反应。在 header 文件中,我有两个声明:一个用于结构,一个用于函数,如下所示:

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

然后我故意将 header 文件两次包含在另一个文件中,其中包含 main() ,看看会发生什么。

令我惊讶的是,编译器抱怨该结构有多个定义。我期望编译器根本不会引发任何多重性错误,因为结构和函数都有纯声明。

只有在我将结构放入 header-guard 之后,编译器才会停止报错。但是,没有为该结构分配内存。这不是一个定义。那为什么编译器疯了?

您不能在一个翻译单元中多次定义结构

您可以在多个翻译单元中定义它,但定义必须相同。 (来源:cppreference/ODR)。

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

使用 include guards(或者如果你的编译器可用的话)pragma 一次。

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

请注意,如果您决定在 header 中提供 k() 定义,则还需要内联声明 k() (如果您需要使用模板而不是指定,这有时是不可避免的)显式模板参数)。

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

编辑:顺便说一句,struct/class 的声明和定义与函数没有太大区别:

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.

在这种情况下,我们没有实现函数的实质,只是说它存在,因为编译器知道签名(或函数承诺采用的内容和 return)它可以继续开心。在 TestStructs 的情况下,前向声明(没有实现)将是

class TestStruct;