如何保护数组定义再次使用非零值进行不完整的初始化?

时间:2013-06-05 19:08:37

标签: c++

我有一个全局数组,它由枚举的值索引,其中有一个元素表示值的数量。必须通过特殊值初始化该数组,遗憾的是该值不是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。

5 个答案:

答案 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,但你没有那个。