c++ - 使用模板和基类实现灵活的数组成员

标签 c++ flexible-array-member

在 C99 中,您通常会看到以下模式:

struct Foo {
    int var1;
    int var2[];
};

Foo * f = malloc(sizeof(struct Foo) + sizeof(int)*n);
for (int i=0; i<n; ++i) {
    f->var2[i] = p;
}

但这不仅是糟糕的 C++,而且还是非法的。

您可以像这样在 C++ 中实现类似的效果:

struct FooBase {
    void dostuff();

    int var1;
    int var2[1];
};

template<size_t N>
struct Foo : public FooBase {
    int var2[N-1];
};

尽管这会起作用(在 FooBase 的方法中,您可以访问 var2[2]var2[3] 等)它依赖于 Foo 是标准布局,不是很漂亮。

这样做的好处是,非模板函数可以接收任何 Foo* 而无需转换,方法是采用 FooBase* 并调用在 var2 上操作的方法,并且内存都是连续的(这很有用)。

是否有更好的方法来实现这一点(这是合法的 C++/C++11/C++14)?

我对这两个琐碎的解决方案不感兴趣(包括在基类中指向数组开头的额外指针,以及在堆上分配数组)。

最佳答案

你想做的事情在 C++ 中是可能的,但并不容易,而且你的 struct 接口(interface)不是 struct 风格的接口(interface)。

就像 std::vector 如何获取一 block 内存并将其重新格式化为非常类似于数组的东西,然后重载运算符以使自己看起来像数组一样,您可以执行相同的操作.

将通过访问器访问您的数据。您将在缓冲区中手动构建您的成员。

您可以从“标签”和数据类型对列表开始。

struct tag1_t {} tag1;
struct tag2_t {} tag2;
typedef std::tuple< std::pair< tag1_t, int >, std::pair<tag2_t, double> > header_t;

然后,我们将解释为“在 header 部分之后,我们有一个数组”的更多类型。我想大量改进这种语法,但现在重要的部分是建立编译时间列表:

struct arr_t {} arr;
std::tuple< header_t, std::pair< arr_t, std::string > > full_t;

然后你必须编写一些模板 mojo,在运行时给定 N,你需要多大的缓冲区来存储 intdouble 后跟 Nstd::string 拷贝,一切都正确对齐。这并不容易。

完成后,您还需要编写代码来构建上述所有内容。如果您想要花哨一些,您甚至可以公开一个完美的转发构造函数和构造函数包装器,从而允许在非默认状态下构造对象。

最后,编写一个接口(interface),根据我注入(inject)到上述tuple中的标记找到构造对象的内存偏移量,reinterpret_cast将原始内存放入对数据类型的引用,并返回该引用(在 const 和非 const 版本中)。

对于末尾的数组,您将返回一些临时数据结构,该结构重载了生成引用的 operator[]

如果您看一下 std::vector 如何将内存块转换为数组,并将其与 boost::mpl 安排标记到数据的方式相结合 map ,然后还手动将事物弄乱以保持正确对齐,每一步都具有挑战性,但并非不可能。我在这里使用的凌乱语法也可以改进(在某种程度上)。

结束界面可能是

Foo* my_data = Foo::Create(7);
my_data->get<tag1_t>(); // returns an int
my_data->get<tag2_t>(); // returns a double
my_data->get<arr_t>()[3]; // access to 3rd one

可以通过一些重载来改进:

Foo* my_data = Foo::Create(7);
int x = my_data^tag1; // returns an int
double y = my_data^tag2; // returns a double
std::string z = my_data^arr[3]; // access to 3rd std::string

但要做到这一点所涉及的工作量相当大,而且所需的许多事情都非常可怕。

基本上,为了解决您所描述的问题,我将不得不在 C++ 中手动重建整个 C++/C 结构布局系统,一旦您完成了,注入(inject)“任意长度数组在结束”。甚至可以在中间注入(inject)任意长度的数组(但这意味着找到该数组后面的结构成员的地址是一个运行时问题:但是,因为我们的 operator^ 被允许运行任意代码,并且您的结构可以存储数组的长度,我们能够做到这一点)。

但是,我想不出一种更简单、可移植的方式来完成您在 C++ 中提出的要求,其中存储的数据类型不必是标准布局。

关于c++ - 使用模板和基类实现灵活的数组成员,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/17424731/

相关文章:

c++ - C++ 库之间的一致性头文件名

c++ - 使用多个标志在 Boost Property Tree 1.50 中使用 read_xml() 时

c++ - 具有多态性的类中灵活的数组成员

c - 如何将两个灵活数组放入一个结构中?

c++ - Pytorch/ATen C++ 中切片张量的等价

C++ 打印盒子应用程序

c++ - 输入std::array [duplicate]时遇到麻烦

c - 在另一个结构中具有灵活数组成员的结构

memory-management - 如何分区和使用与 Rust 一起分配的堆内存?

c - 如何释放存储在结构中的 strdup'd char* 灵活数组成员?