什么是数组模板兼容性的好模式?

What is a good pattern for array template compatibility?

我想创建具有 variable-length 元素数组的 objects,并使它们在 base/derived-class 意义上兼容。在 C 中,可以在 struct 的末尾放置一个不确定的数组,然后只需 malloc object 以包含完整数组:

struct foo {
    int n;
    double x[];
} ;

struct foo *foo1 = (foo *)malloc( sizeof( foo ) + sizeof( double[4] ) );
struct foo *foo2 = (foo *)malloc( sizeof( foo ) + sizeof( double[100] ) );

在 C++ 中,你似乎可以这样做:

template <unsigned I>
class foo {
public:
    int n;
    double x[I];
} ;

但是:

auto foo1 = new foo<4>( );
auto foo2 = new foo<100>( );

if (foo1 == foo2) cerr << "incompatible pointers";

你可以用一个共同的基础class来做到这一点,但这有必要吗?我只想使用 foo1foo2,其中每个 object 都知道其数组的长度。


我的应用程序是针对 ESP32 微控制器的 运行 FreeRTOS。它有有限的 non-virtual RAM 和一个稍微复杂的分配系统,因为各种内存块的能力不同(有些速度较慢,有些不能包含可执行代码,有些不能被 DMA 访问,等等)因此,为 object 的片段分配多个块(例如,通过在末尾对 double 的数组使用 std::vector )变得复杂。

我在 object 构造时知道 double 数组的长度,但我希望 header 和数组位于单个分配的内存块中(所以它可以具有我以后需要的特性。

C-style 做这件事的方式会很好,但是最好有 C++ 特性,比如在数组上迭代(对于各种 objects,每个都有不同数量的doubles)。另外,本机 C++ 解决方案允许我在 x[] 数组中包含 object,而不是在原始分配的内存中放置 new。因此,例如:

auto a[] = { new foo<5>( ), new foo<10>( ), new foo<15>( ) };

for (auto i : a)
    for (auto j : i.x)
        cout << log10( j );    // prints 40 logs of doubles

(我希望它有 C++ 语法错误,但希望它传达了这个想法。如果我能将所有 foo 放入一个公共容器中,我可以弄清楚它的语法。)

作为一名 low-level C++ 开发人员,我完全理解您的需求,遗憾的是,标准 C++ 中的灵活数组成员无法替代,无论是否带有模板。您必须通过编译器扩展继续使用灵活的数组成员。

它们未包含在该语言中,因为在当前形式下,它们本质上是一种 hack。他们在继承或组合方面做得不好。

模板的问题在于,灵活数组版本具有所有大小的通用数组类型。这意味着您可以将它们放在数组中,让 non-template 函数将它们作为参数等:

foo* make_foo(int n);

foo* foos[] = { make_foo(1); make_foo(2); make_foo(3); }; // ok

void take_foo(foo*);

对于模板版本,类型 foo<1>foo<2> 完全无关,因此您不能将它们放在数组中或使用 non-template 接受它们的函数:

template <int N>
foo<N>* make_foo();

auto foos[] = { make_foo<1>(), make_foo<2>(), make_foo<3>() }; // ill-formed

template <int N>
void take_foo(foo<N>*);

std::array 对本次讨论无济于事,继承自某个类型仍会存在不相关类型的问题。

但是,由于这仍然是 C++(尽管 non-standard),您至少可以 一些 额外的细节:

tempate <class T>
struct flex_array {
  int n;
  T data[];

  T* begin() { return &data[0]; }
  T* end() { return begin() + n; }
};


void iterate(flex_array<double>& f) {
    for (double j : f) {
        cout << log10(j); // print however many doubles are in f
    }
}