9

我有一个全局数组,它由一个枚举的值索引,它有一个表示值数量的元素。数组必须由一个特殊值初始化,不幸的是它不是 0。

enum {
  A, B, C, COUNT
};

extern const int arr[COUNT];

在 .cpp 文件中:

const int arr[COUNT] = { -1, -1, -1 };

枚举偶尔会更改:添加新值,删除一些值。我刚刚修复的代码中的错误是初始化值的数量不足,这导致数组的其余部分被初始化为零。我想防范这种错误。

问题是要么保证arr始终使用特殊值(-1示例中的 the )完全初始化,要么中断编译以引起开发人员的注意,因此可以手动更新数组。

最近的 C++ 标准不可用(旧的 ms 编译器和一些专有的垃圾)。在一定程度上可以使用模板。强烈禁止 STL 和 Boost(不要问),但我不介意复制或重新实现所需的部分。

如果事实证明这是不可能的,我将不得不考虑将特殊值更改为 0,但我想避免这种情况:特殊值 (the -1) 可能有点太特殊并在其余代码中隐式编码.

我想避免 DSL 和代码生成:主要的构建系统是 ms windows 上的堵塞,主要 PITA 在那里生成任何东西。

4

5 回答 5

7

我能想出的最佳解决方案是替换arr[COUNT]arr[],然后编写一个模板来断言sizeof(arr) / sizeof(int) == COUNT. 这不能确保它被初始化为-1,但它会确保您已使用正确数量的元素显式初始化数组。

C++11static_assert会更好,或者 Boost 的宏版本,但如果你没有可用的,你将不得不自己想出一些东西。

于 2013-06-05T19:11:54.273 回答
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]”类型的引用无效初始化</p>

对于某些编译器,您可能需要命名临时

const int arr_init[] = { -1, -1, -1};
const int (&arr)[COUNT] = arr_init;

更新

我被告知第一个=(int[]){-1,-1,-1}版本是编译器扩展,因此=arr_init;首选第二个版本。

于 2013-06-07T18:02:02.240 回答
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提出使用变量数组来计算值的想法

于 2013-06-05T20:06:50.867 回答
1

Boost.Preprocessor库可能会提供一些有用的东西,但我怀疑您是否会被允许使用它,而且从 Boost 源中提取它可能会变得笨拙。

这个类似的问题有一个看起来很有帮助的答案: 技巧:使用宏填充数组值(代码生成)

于 2013-06-05T20:01:57.820 回答
0

最接近初始化而不是检查的方法是使用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};直到运行时才会发生。如果编译器正在优化初始化,那么您依赖的是implementation,而不是标准。

我在这里真正要强调的一点是:int arr[COUNT] = {-1, -1, -1};除非可以优化,否则仍然是运行时初始化。您可以依靠它保持不变的唯一方法是使用 C++11 constexpr,但您没有可用的。

于 2013-06-06T20:54:57.963 回答