也许是这样的?
是:这利用了未知行为的黑魔法。
然而: AFAIK这是要求问题静态执行此操作的唯一方法。
问:这是一个好主意还是应该以不同的方式完成?
- 答:这取决于情况。
struct Buff {
int len;
char const *ptr;
};
struct Collection {
int num;
Buff buffers[1];
};
template <unsigned S>
struct CollectionSize : public Collection {
private:
Buff extraspace[t-1];
};
static const Collection &my_collestions[] = {
CollectionSize<1>(), // add more constructor arguments so you can initialize the base class. the object is const so this is your last chance to do so.
CollectionSize<2>(),
CollectionSize<3>(),
CollectionSize<4>(),
CollectionSize<5>()
};
说明:
关键是你抛开通过非类型模板的派生类的额外空间,但你通过基类数组访问它。你过度阅读下一个对象,但内存将在那里,是的,这是未定义的行为,但实际上这将工作。你也可以使数组成为const references
而不是对象本身或指针。您需要引用或指针,以便可以将对象切片到其基类,因为该数组需要是同类型的。这非常重要,因为C++标准对于临时对象的const引用有特殊的规则(也就是说,只要引用在附近时它们就会一直存在),这就避免了你必须调用new
来动态分配CollectionSize
对象。
回顾的关键组件:由常量引用常量引用坚持围绕引用
- 无名的临时对象(仍然有效的对象,还是分配,与析构函数还未调用)只要引用在const范围内(在这种情况下,静态引用在范围内,直到程序退出)(不能写入它们)。
- 制作派生模板类型的对象意味着您可以在对象内部为基类分配更多空间(这由标准未定义,但在实际中适用于所有编译器AFAIK)。
- 由于在实践中编译器定义了一些没有虚拟指针的单继承类的对象布局,所以已知派生类的内存直接位于派生类的最后一个成员数组之后。访问这个内存不是标准定义的,但会在实践中使用。
编辑:
我已经收到了这个答案了一些批评,所以我张贴一些额外的意见,以解决它
我开头这个答案的事实,它可能涉足不确定的,但很OK-的实践。这就是编译器是如何做到的!因此,现实的只有两件事情,我们担心是:
- 对象布局,并确保有足够的内存,以便阵列访问。虽然在C++中没有定义对象布局,但是编译器供应商在做这件事时做得非常好,并且它是一致的,至少对于没有虚拟指针的类的单一继承是一致的。
- 会员将按顺序排列。
- 派生类将在基类之后的内存中。 所以,即使有填充也没关系,因为在对象的末尾至少需要这么多的空间,并且可以通过基类以一致的方式操纵它。
- C++中的规则约为当您访问您不拥有的内存时会发生什么。这个数组技巧在C中是非常常见的事情。通常它采用一个简单结构的形式,其中一个数组作为大小为0或1的元素的最后一个成员,程序员调用malloc来获得较大的大小,然后通过访问结束内存结构的数组。
在实际的实施和我自己的经验,我从来没有遇到过任何一个行为不如预期的情况。这对程序员来说是最好的选择吗?很可能它不是,但它又取决于它。也许STL向量,或者甚至指针的额外的间接层都不是某个随机低端系统的选项。也许这些对象确实需要为了优雅的代码而放入数组中。
无论如何,问题的提出方式是如何使这样的结构静态初始化,而不是天气或不是一个好主意。 AFAIK这是做到这一点的最佳解决方案。然而,更好地实现这一点的其他解决方案当然受到欢迎
您应该向Collection中添加一个构造函数,该构造函数将#个缓冲区作为参数,然后在构造函数中初始化缓冲区。 – Steger
@Steger我不需要在那个结构中做任何改变,所以我不希望结构在运行时自行构造。 – pavelkolodin
但是你需要一个可变数量的缓冲区,对吧?所以你可以为Collection定义一个构造函数,或者在Collection中创建一个模板。 – Steger