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

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

本文关键字:ODR C++ 结构 声明 为什么      更新时间:2023-10-16

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

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

然后我故意在另一个带有main((的文件中包含两次头文件,看看会发生什么。

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

只有在我将结构放入标头保护中后,编译器才会停止抱怨。但是,没有为结构分配内存。它不是一个定义。那编译器为什么会生气呢?

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

您可以在多个翻译单元中定义它,但定义必须相同。(资料来源:cpp偏好/ODR(。

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

使用包含保护(或者如果您的编译器可用(编译指示一次。

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

请注意,为了避免 muldefs,如果您决定在标头中提供其定义,您还需要内联声明 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
    }
};

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

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;