为什么我不能在公共 header 文件中初始化静态 class 成员?
Why can't I initialize a static class member in a common header file?
假设我有一个只有 header 的图书馆。我把它简化成这样。
Header 只有图书馆 Foo.hpp
#ifndef FOO_HPP
#define FOO_HPP
struct Foo{
static const int A;
};
const int Foo::A = 100;
void SomeMethod(){
// do some stuff
}
#endif
然后,我有 parent class(Parent.hpp
和 Parent.cpp
):
#ifndef PARENT_HPP
#define PARENT_HPP
#include "Foo.hpp"
struct Parent {
virtual void my_method();
};
#endif
Child class(Child.hpp
和 Child.cpp
)
#ifndef CHILD_HPP
#define CHILD_HPP
#include "Parent.hpp"
#include "Foo.hpp"
struct Child : Parent{
void my_method();
};
#endif
在 my_method()
中,我只打印 Foo::A
变量。
在实际代码库中,我在 header 唯一库中有模板。
当我编译这个时,我得到了一个“多重定义”错误。如何解决这个问题?
替换
class Foo {
public:
static const int A;
};
const int Foo::A = 100;
和
class Foo {
public:
static const int A = 100;
};
您选择了“non-inline”模式来初始化您的静态成员。这是完全合法的,但是 - 在这种情况下,您需要有一个定义您的成员的翻译单元(例如编译的 .cpp
文件)。否则,C++ 链接器会在 parent.o
中看到 Foo::A
的一个定义,在 child.o
中看到第二个定义,并且这些定义会发生冲突。
因此,
解决方案 1:将定义移动到另一个文件
- 创建一个
Foo.cpp
,其中包含 Foo.hpp
并定义 Foo::A
。
- 从 header 中删除定义,因此它不会在多个位置重复
解决方案 2:使定义有条件
这不是真的推荐,但确实有效。
像这样包围定义:
#ifdef FOO_A_DEFINITION
const int Foo::A = 100;
#endif
创建一个Foo.cpp
,定义#FOO_A_DEFINITION
然后包含Foo.hpp
这对使用宏有不利影响,但人类用户可以从 header 中看到定义。
解决方案 3:在 class 定义中初始化
所以,您问自己“为什么它们会发生冲突?我希望编译器知道它们是同一个变量!”
一种方法是初始化 class body 中的静态成员。由于这是一种简单类型,因此即使使用旧版本的语言标准也是可能的。这也是@idmean's suggested solution:
class Foo {
public:
static const int A = 100;
};
这里有一个警告,即您“不是真的”以这种方式定义静态成员。您可以使用它,但不是以任何方式使用用解决方案 (1.) 或 (2.) 定义的变量。请参阅此处的讨论:
在此处查看对这一点的讨论:
Defining static const integer members in class definition
解决方案 4:内联您的静态成员
这个解决方案是另一种告诉编译器“它只是每个 body 的一个定义”的方法,它只适用于 C++17 或更高版本:
#include <iostream>
class Foo {
public:
inline static const int A = 100;
};
这看起来与解决方案 3 非常相似,但实际上做了一些非常不同的事情!
使用此解决方案,您实际上是在每个翻译单元中多次定义静态成员,但告诉编译器在链接和遇到许多静态成员时只保留一个定义。
当您使用 dynamically-linked 库时,解决方案 (1.) 和 (2.) 的行为与此解决方案 (4.) 不同。参见:
Where to initialize static const member in c++17 or newer?
求解释。
假设我有一个只有 header 的图书馆。我把它简化成这样。
Header 只有图书馆 Foo.hpp
#ifndef FOO_HPP
#define FOO_HPP
struct Foo{
static const int A;
};
const int Foo::A = 100;
void SomeMethod(){
// do some stuff
}
#endif
然后,我有 parent class(Parent.hpp
和 Parent.cpp
):
#ifndef PARENT_HPP
#define PARENT_HPP
#include "Foo.hpp"
struct Parent {
virtual void my_method();
};
#endif
Child class(Child.hpp
和 Child.cpp
)
#ifndef CHILD_HPP
#define CHILD_HPP
#include "Parent.hpp"
#include "Foo.hpp"
struct Child : Parent{
void my_method();
};
#endif
在 my_method()
中,我只打印 Foo::A
变量。
在实际代码库中,我在 header 唯一库中有模板。
当我编译这个时,我得到了一个“多重定义”错误。如何解决这个问题?
替换
class Foo {
public:
static const int A;
};
const int Foo::A = 100;
和
class Foo {
public:
static const int A = 100;
};
您选择了“non-inline”模式来初始化您的静态成员。这是完全合法的,但是 - 在这种情况下,您需要有一个定义您的成员的翻译单元(例如编译的 .cpp
文件)。否则,C++ 链接器会在 parent.o
中看到 Foo::A
的一个定义,在 child.o
中看到第二个定义,并且这些定义会发生冲突。
因此,
解决方案 1:将定义移动到另一个文件
- 创建一个
Foo.cpp
,其中包含Foo.hpp
并定义Foo::A
。 - 从 header 中删除定义,因此它不会在多个位置重复
解决方案 2:使定义有条件
这不是真的推荐,但确实有效。
像这样包围定义:
#ifdef FOO_A_DEFINITION const int Foo::A = 100; #endif
创建一个
Foo.cpp
,定义#FOO_A_DEFINITION
然后包含Foo.hpp
这对使用宏有不利影响,但人类用户可以从 header 中看到定义。
解决方案 3:在 class 定义中初始化
所以,您问自己“为什么它们会发生冲突?我希望编译器知道它们是同一个变量!”
一种方法是初始化 class body 中的静态成员。由于这是一种简单类型,因此即使使用旧版本的语言标准也是可能的。这也是@idmean's suggested solution:
class Foo {
public:
static const int A = 100;
};
这里有一个警告,即您“不是真的”以这种方式定义静态成员。您可以使用它,但不是以任何方式使用用解决方案 (1.) 或 (2.) 定义的变量。请参阅此处的讨论:
在此处查看对这一点的讨论:
Defining static const integer members in class definition
解决方案 4:内联您的静态成员
这个解决方案是另一种告诉编译器“它只是每个 body 的一个定义”的方法,它只适用于 C++17 或更高版本:
#include <iostream>
class Foo {
public:
inline static const int A = 100;
};
这看起来与解决方案 3 非常相似,但实际上做了一些非常不同的事情!
使用此解决方案,您实际上是在每个翻译单元中多次定义静态成员,但告诉编译器在链接和遇到许多静态成员时只保留一个定义。
当您使用 dynamically-linked 库时,解决方案 (1.) 和 (2.) 的行为与此解决方案 (4.) 不同。参见:
Where to initialize static const member in c++17 or newer?
求解释。