【问题标题】:Why can't I initialize a static class member in a common header file?为什么我不能在公共头文件中初始化静态类成员?
【发布时间】:2021-01-23 11:02:56
【问题描述】:

假设我有一个只有标题的库。我已经把它简化成这样了。

仅标题库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.hppParent.cpp):

#ifndef PARENT_HPP
#define PARENT_HPP

#include "Foo.hpp"

struct Parent {
        virtual void my_method();
};
#endif

儿童班(Child.hppChild.cpp

#ifndef CHILD_HPP
#define CHILD_HPP

#include "Parent.hpp"
#include "Foo.hpp"

struct Child : Parent{
        void my_method();
};
#endif  

my_method() 中,我只打印Foo::A 变量。

在真正的代码库中,我在仅标头库中有模板。

当我编译这个时,我得到一个“多重定义”错误。如何解决这个问题?

【问题讨论】:

  • 您应该提供一个正确的工作示例,以及您收到的确切错误消息。另外,请说明您使用的是哪个编译器以及哪个版本的语言标准。
  • 我试图提供一整套 (?) 替代方案供您考虑。

标签: c++ static-members multiple-definition-error


【解决方案1】:

您选择了初始化静态成员的“非内联”模式。这是完全合法的,但是 - 在这种情况下,您需要只有一个翻译单元(例如编译的 .cpp 文件)来定义您的成员。否则,C++ 链接器在parent.o 中看到Foo::A 的一个定义,在child.o 中看到第二个定义,并且这些冲突。

因此,

解决方案 1:将定义移至另一个文件

  1. 创建一个Foo.cpp,其中包括Foo.hpp并定义Foo::A
  2. 从标题中删除定义,以免在多个位置重复

解决方案 2:使定义有条件

这不是真的推荐,但它确实有效。

  1. 像这样包围定义:

    #ifdef FOO_A_DEFINITION
    const int Foo::A = 100;
    #endif
    
  2. 创建一个Foo.cpp,它定义#FOO_A_DEFINITION,然后包含Foo.hpp

这对使用宏不利,但有利于人类用户从标题中看到定义。

方案3:在类定义中初始化

所以,您是在问自己“为什么它们会发生冲突?我希望编译器知道它们是同一个变量!”

一种方法来初始化类体内的静态成员。由于这是一个简单的类型,即使使用旧版本的语言标准也是可能的。这也是@idmean's suggested solution

class Foo {
    public:
        static const int A = 100;
};

这里有一个警告,那就是你“不是真的”以这种方式定义静态成员。您可以使用它,但并非以所有方式使用由解决方案 (1.) 或 (2.) 定义的变量。请参阅此处的讨论:

在此处查看关于这一点的讨论:

Defining static const integer members in class definition

解决方案 4:内联静态成员

这个解决方案是另一种告诉编译器“它只是每个人的定义”的方式,它只适用于 C++17 或更高版本:

#include <iostream>
class Foo {
    public:
        inline static const int A = 100;
};

这看起来与解决方案 3 非常相似,但实际上做了一些非常不同的事情!

使用此解决方案,您实际上是在每个翻译单元中多次定义静态成员,但告诉编译器在链接和遇到其中许多时只保留一个定义。

当您使用动态链接库时,解决方案 (1.) 和 (2.) 的行为与此解决方案 (4.) 不同。见:

Where to initialize static const member in c++17 or newer?

解释一下。

【讨论】:

    【解决方案2】:

    替换

    class Foo {
        
        public:
            static const int A;
        
    };
    
    const int Foo::A = 100;
    

    class Foo {
        
        public:
            static const int A = 100;
        
    };
    

    【讨论】:

    • 这只是一个简化的例子,但还定义了一些其他方法,这些方法不在类中。这些方法是模板方法。
    • @Bharata 好吧,您必须内联您在标题中定义的所有内容。否则你会得到链接器错误。
    • 我在hpp头文件中添加了SomeMethod的例子。有没有办法解决这个问题?
    • @Bharata 将 void SomeMethod(){ 更改为 inline void SomeMethod(){
    • 好的,我试试看。
    猜你喜欢
    • 2022-08-06
    • 2012-03-28
    • 2013-10-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多