为什么结构声明违反了 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;
我试图让编译器对一些我认为不违反 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;