我有一个全局数组,它由枚举的值索引,其中有一个元素表示值的数量。必须通过特殊值初始化该数组,遗憾的是该值不是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窗口上是堵塞的,并且在那里生成任何东西都是主要的PITA。
答案 0 :(得分:7)
我能提出的最佳解决方案是将arr[COUNT]
替换为arr[]
,然后编写一个模板来断言sizeof(arr) / sizeof(int) == COUNT
。这不能确保它被-1
初始化,但它将确保您使用正确数量的元素显式初始化数组。
C ++ 11的static_assert
会更好,或者Boost的宏版本,但是如果你没有,你就必须自己想出一些东西。
答案 1 :(得分:5)
这很容易。
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 [2]'类型的表达式初始化'const int(&)[3]'类型的引用无效
对于某些编译器,您可能需要命名临时
const int arr_init[] = { -1, -1, -1};
const int (&arr)[COUNT] = arr_init;
我被告知第一个=(int[]){-1,-1,-1}
版本是编译器扩展,因此第二个=arr_init;
版本是首选。
答案 2 :(得分:2)
回答我自己的问题:虽然似乎不可能直接为数组提供适当数量的初始化器,但只需测试适当数量的初始化器列表就很容易:
#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 the idea to use a variable array to count the values。
答案 3 :(得分:1)
Boost.Preprocessor库可能提供一些有用的东西,但我怀疑你是否会被允许使用它,并且从Boost源中提取它可能会变得难以处理。
这个类似的问题有一个看起来很有用的答案: Trick : filling array values using macros (code generation)
答案 4 :(得分:0)
我最接近初始化而不是检查是对数组使用const
引用,然后在全局对象中初始化该数组。它仍然是运行时初始化,但idk你如何使用它所以这可能是足够好的。
#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
,但你没有那个。