我有一个全局数组,它由枚举的值索引,其中有一个元素表示值的数量。数组必须用一个特殊值初始化,不幸的是这个值不是0。
enum {
A, B, C, COUNT
};
extern const int arr[COUNT];
在。cpp文件中:
const int arr[COUNT] = { -1, -1, -1 };
枚举偶尔会被更改:添加新值,删除一些值。我刚刚修复的代码中的错误是初始化值的数量不足,这导致数组的其余部分用零初始化。我想对这种错误采取保护措施。
问题是要么保证arr
总是用特殊值(示例中的-1
)完全初始化,要么中断编译以引起开发人员的注意,因此可以手动更新数组。
最新的c++标准不可用(旧的ms编译器和一些私有垃圾)。在某种程度上,可以使用模板。STL和Boost是强烈禁止的(不要问),但我不介意复制或重新实现所需的部分。
如果结果证明是不可能的,我将不得不考虑将特殊值更改为0,但我想避免这种情况:特殊值(-1
)可能有点太特殊了,并且在其余代码中隐式编码。
我想避免DSL和代码生成:主要构建系统在ms windows上卡住了,在那里生成任何东西都是主要的PITA。
我能想到的最好的解决方案是将arr[COUNT]
替换为arr[]
,然后编写一个模板来断言sizeof(arr) / sizeof(int) == COUNT
。这并不能确保它被初始化为-1
,但它可以确保您已经用正确的元素数量显式初始化了数组。
c++ 11的static_assert
会更好,或者Boost的宏版本,但如果你没有可用的,你必须自己想出一些东西。
这很简单。
enum {
A, B, C, COUNT
};
extern const int (&arr)[COUNT];
const int (&arr)[COUNT] = (int[]){ -1, -1, -1};
int main() {
arr[C];
}
乍一看,这似乎会产生开销,但是当您仔细检查它时,它只是为编译器关心的同一个变量生成两个名称。所以没有开销。
这里它是工作的:http://ideone.com/Zg32zH,下面是错误情况下发生的情况:http://ideone.com/yq5zt3
prog.cpp:6:27:错误:const int (&)[3]类型引用的初始化无效
const int[2]类型表达式
对于某些编译器,您可能需要命名临时
const int arr_init[] = { -1, -1, -1};
const int (&arr)[COUNT] = arr_init;
<标题> 更新我被告知第一个=(int[]){-1,-1,-1}
版本是一个编译器扩展,所以第二个=arr_init;
版本是首选。
回答我自己的问题:虽然直接为数组提供适当数量的初始化式似乎是不可能的,但测试初始化式列表以获得适当数量的初始化式确实很容易:
#define INITIALIZERS -1, -1, -1,
struct check {
check() {
const char arr[] = {INITIALIZERS};
typedef char t[sizeof(arr) == COUNT ? 1: -1];
}
};
const int arr[COUNT] = { INITIALIZERS };
感谢@dauphic使用变量数组来计算值的想法。
预处理器库可能会提供一些有用的东西,但我怀疑您是否会被允许使用它,并且从Boost源中提取它可能会变得笨拙。
这个类似的问题有一个看起来很有用的答案:技巧:使用宏填充数组值(代码生成)
我能得到的最接近初始化而不是检查的方法是使用对数组的const
引用,然后在全局对象中初始化该数组。它仍然是运行时初始化,但我知道你如何使用它,所以这可能足够好。
#include <cstring>
enum {A, B, C, COUNT};
namespace {
class ArrayHolder {
public:
int array[COUNT]; // internal array
ArrayHolder () {
// initialize to all -1s
memset(this->array, -1, sizeof(this->array));
}
};
const ArrayHolder array_holder; // static global container for the array
}
const int (&arr)[COUNT] = array_holder.array; // reference to array initailized
// by ArrayHolder constructor
你仍然可以像以前一样使用sizeof
:
for (size_t i=0; i < sizeof(arr)/sizeof(arr[0]); ++i) {
// do something with arr[i]
}
编辑如果运行时初始化永远不能依赖,你应该检查asm中的实现细节,因为即使使用初始化器声明arr
的值,在运行时初始化之前可能仍然不知道
const int arr[1] = {5};
int main() {
int local_array[arr[0]]; // use arr value as length
return 0;
}
用g++ -pedantic
编译给出警告:
warning: ISO C++ forbids variable length array ‘local_array’ [-Wvla]
另一个编译失败的例子:
const int arr1[1] = {5};
int arr2[arr1[0]];
error: array bound is not an integer constant before ']' token
至于使用数组值作为全局构造函数的参数,这里的两个构造函数调用都是可以的:
// [...ArrayHolder definition here...]
class IntegerWrapper{
public:
int value;
IntegerWrapper(int i) : value(i) {}
};
const int (&arr)[COUNT] = array_holder.array;
const int arr1[1] = {5};
IntegerWrapper iw1(arr1[0]); //using = {5}
IntegerWrapper iw2(arr[0]); //using const reference
此外,跨不同源文件的全局变量的初始化顺序没有定义,您不能保证arr = {-1, -1, -1};
直到运行时才会发生。如果编译器正在优化初始化,那么您依赖的是实现,而不是标准。
这里我真正想强调的一点是:int arr[COUNT] = {-1, -1, -1};
仍然是运行时初始化,除非它可以被优化出来。你可以依赖它是常量的唯一方法是使用c++ 11的constexpr
,但你没有可用的