在不使用模板的情况下获取 std::array 引用

Getting an std::array reference without using templates

本文关键字:std 获取 array 引用 情况下      更新时间:2023-10-16

如果一个函数接收一个数组范围(开始/结束(作为指针,有没有办法再次将其视为std::array

void xyz(int* begin, int* end) {
// at this point i know that at this memory range there's
// an array<int> of end - begin size
// is there any way i can get that back?
}
int main() {
array<int, 5> x = {1, 2, 3, 4, 5};
xyz(x.begin(), x.end());
}

我知道我可以x作为参数传递,但我必须指定它的大小。解决方法是使用模板,但我想知道有没有一种方法可以在不使用模板和不复制它的情况下重建对x的类型化引用?

如果(开始/结束(不足以重建xyz内部的类型xyz有没有其他方法可以提供数组的内存表示形式,并让它重建类型......相信它在这个内存地址上?

std::array

被实现为一个struct,将 C 样式的固定长度数组作为其唯一的数据成员。 迭代器begin()通常是指向数组1的第一个元素的原始指针。

1:@bitmask评论中提出了一个很好的观点:"请注意,std::array<T,N>::iterator不一定是原始指针。通常是,但不一定是。也不必转换为T*。因此,由于编译器的实现细节,您的代码C++无效并进行编译。

数组的第一个元素的地址保证与数组本身的地址相同。并且结构的第一个成员的地址保证与结构本身的地址相同。 因此,从理论上讲,假设begin迭代器实际上是指向第一个数组元素的原始指针,那么您应该能够将begin类型转换为std::array*指针 - 但前提是您在编译时知道用于实例化该std::arrayEXACT模板参数值,例如:

void xyz(int* begin, int* end) {
using array_type = array<int, 5>; // OK
array_type *arr = reinterpret_cast<array_type*>(begin);
...
}
int main() {
array<int, 5> x = {1, 2, 3, 4, 5};
xyz(x.begin(), x.end());
}

但是,如果你不能保证begin只来自一个std::array,并且是指向其第一个元素的原始指针,这将惨败。

虽然您可以从开始传入的迭代器类型中确定必要的T模板参数,但不能仅使用迭代器来确定必要的N模板参数。 该值必须是编译时常量,但获取 2 个迭代器之间的距离只能在运行时确定:

void xyz(int* begin, int* end) {
using array_type = array<int, ???>; // <-- can't use (end-begin) here!!!
array_type *arr = reinterpret_cast<array_type*>(begin);
...
}

或:

template<typename Iter>
void xyz(Iter begin, Iter end) {
using array_type = array<iterator_traits<Iter>::value_type, ???>; // <-- can't use (end-begin) here!!!
array_type *arr = reinterpret_cast<array_type*>(begin);
...
}

首先使用迭代器作为函数参数的全部意义在于抽象出容器类型,使其未知或不需要。 因此,从迭代器中收集的信息确定原始容器类型是非常困难的,如果不是不可能的话。

因此,在这种情况下,如果您确实需要访问std::array那么最好只是传递整个std::array本身,而不是它的迭代器:

template<typename T, size_t N>
void xyz(array<T, N> &arr) {
...
}
int main() {
array<int, 5> x = {1, 2, 3, 4, 5};
xyz(x);
}