在头文件中初始化常量静态数组

51

我刚刚发现以下内容是无效的。

//Header File
class test
{
    const static char array[] = { '1', '2', '3' };
};

最好的初始化位置在哪里?

6个回答

57

最佳位置应该在源代码文件中

// Header file
class test
{
    const static char array[];
};

// Source file
const char test::array[] = {'1','2','3'};

你可以像你尝试的那样在类声明中初始化整数类型; 所有其他类型必须在类声明外进行初始化,且仅需一次。


这句话不应该是“...在类的声明中...”吗?我认为.h文件是声明,.c文件是定义,因此仅在头文件中声明整数类型并将其引用会导致编译器错误:undefined reference to test::SOME_INTEGER。 (我意识到这听起来非常苛刻和学究,但我并不想故意制造麻烦;我只是想确保我使用正确的术语,所以如果我错了,请务必纠正我)。 - dwanderson

38
您始终可以执行以下操作:
class test {
  static const char array(int index) {
    static const char a[] = {'1','2','3'};
    return a[index];
  } 
};

这种编程范式有几个好处:

  • 不需要cpp文件
  • 如果需要,可以进行范围检查
  • 避免了担心静态初始化失败的问题

1
我无法让编译器在多个对象上保持&a[1]的一致性。 - Alexander Oh
对于字符串文字,这是完美的! - sage
4
你提供的链接“static initialization fiasco”已经失效。 - sergiol
它使用锁来访问a[]。 - Nick
2
@sergiol修复了死链(三年后)。 - JKD

21
//Header File 
class test 
{ 
    const static char array[];
}; 

// .cpp
const char test::array[] = { '1', '2', '3' }; 

谢谢,不确定你是否能在成员之外完成这个。 - user174084
8
请勿在定义中加入静态内容。 - anon
3
因为他们没有尝试编译它。^ _- 不过,说真的,“static”有很多用途,让很多人感到困惑。 - Mike DeSimone
抱歉,那是一个复制粘贴错误,已经修正。 - peterchen

12

现在,在C++17中,你可以使用内联变量

如何使用内联变量?

一个简单的静态数据成员(N4424):

struct WithStaticDataMember {
  // This is a definition, no out­of­line definition is required.
  static inline constexpr const char *kFoo = "foo bar";
};
在你的例子中:

//Header File
class test
{
    inline constexpr static char array[] = { '1', '2', '3' };
};

应该只需要工作


2
我认为 static constexpr 已经包含了 inline 的意思:https://dev59.com/HmYq5IYBdhLWcg3wjBQM#57407675 - Ciro Santilli OurBigBook.com

2

使用 constexpr,即使在 C++11 中也必须在头文件中定义值

如果您使用 constexpr 而不是 const,那么这个答案建议您甚至必须在头文件中定义即使在 C++11 中

#include <cassert>

struct MyClass {
    static constexpr int is[] = {1, 2, 3};
    static constexpr int i = 1;
};

// TODO is this ever mandatory? Create example that fails on -std=c++11.
// Pretty sure never mandatory in C++17 https://dev59.com/sHI95IYBdhLWcg3w3h_G#40959093
// constexpr int MyClass::is[];

int main (void) {
    assert(MyClass::is[0] == 1);
    assert(&MyClass::is[0] == &MyClass::is[1] - 1);
    assert(MyClass::i == 1);
    assert(&MyClass::i == &MyClass::i);
}

编译并运行的方法:

g++-10 -ggdb3 -O0 -std=c++11 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out 

如果您尝试以下方式:
struct MyClass {
    static constexpr int is[];
};

constexpr int MyClass::is[] = {1, 2, 3};

编译失败,错误信息如下:

main.cpp:4:26: error: ‘constexpr’ static data member ‘is’ must have an initializer

在Ubuntu 20.04上测试通过。


0
这有点滥用系统,但如果你真的想在头文件中定义它(而且你没有使用C++17),你可以这样做。它不会是一个静态成员,但它将是一个常量,仅占用每个编译单元的存储空间(而不是每个类实例):
(将所有这些代码放在头文件中。)
namespace {
    const char test_init_array[] = {'1', '2', '3'};
}

class test {
public:
    const char * const array;

    test() : array(test_init_array) {}
};

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接