什么是数组模板兼容性的好模式?
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来做到这一点,但这有必要吗?我只想使用 foo1
和 foo2
,其中每个 object 都知道其数组的长度。
我的应用程序是针对 ESP32 微控制器的 运行 FreeRTOS。它有有限的 non-virtual RAM 和一个稍微复杂的分配系统,因为各种内存块的能力不同(有些速度较慢,有些不能包含可执行代码,有些不能被 DMA 访问,等等)因此,为 object 的片段分配多个块(例如,通过在末尾对 double
的数组使用 std::vector
)变得复杂。
我在 object 构造时知道 double
数组的长度,但我希望 header 和数组位于单个分配的内存块中(所以它可以具有我以后需要的特性。
C-style 做这件事的方式会很好,但是最好有 C++ 特性,比如在数组上迭代(对于各种 objects,每个都有不同数量的double
s)。另外,本机 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
}
}
我想创建具有 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来做到这一点,但这有必要吗?我只想使用 foo1
和 foo2
,其中每个 object 都知道其数组的长度。
我的应用程序是针对 ESP32 微控制器的 运行 FreeRTOS。它有有限的 non-virtual RAM 和一个稍微复杂的分配系统,因为各种内存块的能力不同(有些速度较慢,有些不能包含可执行代码,有些不能被 DMA 访问,等等)因此,为 object 的片段分配多个块(例如,通过在末尾对 double
的数组使用 std::vector
)变得复杂。
我在 object 构造时知道 double
数组的长度,但我希望 header 和数组位于单个分配的内存块中(所以它可以具有我以后需要的特性。
C-style 做这件事的方式会很好,但是最好有 C++ 特性,比如在数组上迭代(对于各种 objects,每个都有不同数量的double
s)。另外,本机 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
}
}