如何从向量初始化数组?(如何将指针强制转换为数组?)

How to initialize an array from a vector ? (How to cast a pointer to array ?)

本文关键字:数组 转换 向量 初始化 指针      更新时间:2023-10-16

我正试图从std::vector的数据构建std::array。我目前已经这样做了:

#include <vector>
#include <array>
int main(void)
{
    std::vector<int>    vec{{1, 2, 3, 4, 5}};
    std::array<int, 5>  arr{static_cast<int [5]>(vec.data())};
    (void)arr;
    return 0;
}

但是gcc不接受这个铸造:

错误:从类型"int*"到类型"int[5]"的static_cast无效

我认为数组可以用作指针,那么为什么我们不能进行这种强制转换呢?

编辑:这不是将std::vector复制到std::array的重复,因为我的问题是关于std::array初始化(构造);不复制到其中。

编辑:我已经这样做了:

#include <vector>
#include <array>
int main(void)
{
    std::vector<int>    vec{{1, 2, 3, 4, 5}};
    int                 *a(vec.data());
    std::array<int, 5>  arr{*reinterpret_cast<int (*)[5]>(&a)};
    (void)arr;
    return 0;
}

但是数组没有初始化。。。gcc表示:

错误:必须使用大括号括起来的初始值设定项初始化数组

"我以为数组可以用作指针,为什么我们不能进行此强制转换?"

您可以使用数组作为指针,因为它们会"衰减"到它们。请参阅"什么是阵列衰减?"

但事实并非如此。(在不同大小的阵列上也有类型检查。)

虽然static_cast可以用于反转一些隐式转换,但这不是其中之一。它被明确排除在列表之外:

5) 如果存在从new_type到表达式类型的标准转换序列,则不包括左值到右值、数组到指针、函数到指针、空指针、空成员指针、函数指针(自C++17以来)或布尔转换,则static_cast可以执行该隐式转换的逆转换。

因此,这可能会让您想知道如何将指针转换为数组类型。你不能,因为"数组在C中是二等公民(因此在C++中也是如此)。"reinterpret_cast也不起作用。

(如果您尝试使用像(int [5])(vec.data())这样的C样式强制转换,您可能会得到更明确的消息ISO C++禁止强制转换为数组类型"int[5]"。)

这就是语言实现失败的原因。在这里,您要做的是尝试初始化一个std::array,它的存在理由是真正包装一个C数组:

此容器是一个聚合类型,其语义与持有C样式数组T[N]作为其唯一非静态数据成员的结构相同。

因此,当涉及到初始化表单时,它将无法执行任何C数组无法执行的操作。如果你手里有一个整数指针,并且想从该指针初始化一个int arr[5] = ...,那么你同样运气不佳。你必须复制它。

在这种情况下,可以用。。。

将std::vector复制到std::array

您可以使用递归模板来实现与您想要的类似的功能。诀窍是使用…将参数列表构造为数组构造函数。。。变量模板中的运算符,该模板具有所需的向量元素索引作为其变量参数。在下面实现这一点的实现中,数组本身的构建是在模板中完成的,您可以依靠返回值优化来确保不进行复制以将值获取到最终数组中。这实现了从矢量初始化数组的既定目标,尽管这有点人为,不一定是你真正想做的事情

#include <vector>
#include <array>
#include <iostream>
template<typename T, unsigned total_elem_count, unsigned remain_elem_count, unsigned... elements>
struct init_array_from_vec_helper
{   
    static std::array<T, total_elem_count> array_from_vec(const std::vector<T>& cont)
    {   
        return init_array_from_vec_helper<T, total_elem_count, remain_elem_count-1, remain_elem_count-1, elements...>::array_from_vec(cont);
    }   
};  

template<typename T, unsigned total_elem_count, unsigned... elements>
struct init_array_from_vec_helper<T, total_elem_count, 0, elements...>
{   
    static std::array<T, total_elem_count> array_from_vec(const std::vector<T>& cont)
    {   
        return std::array<T, total_elem_count>{cont[elements]...};
    }   
};  
template<typename T, unsigned total_elem_count>
std::array<T, total_elem_count> init_array_from_vec(const std::vector<T>& vec)
{   
    return init_array_from_vec_helper<int, total_elem_count, total_elem_count-1, total_elem_count-1>::array_from_vec(vec);
}   
int main(void)
{   
    std::vector<int>    vec{{1, 2, 3, 4, 5}};
    std::array<int, 5> arr(init_array_from_vec<int, 5>(vec));
    (void)arr;
    for(int i : arr)
        std::cout << i << std::endl;
    return 0;
}   

std::array是一个聚合类型;它没有任何用户定义的构造函数。它的单个数据成员是一个数组,不能在函数或构造函数调用中传递;通过值传递的数组衰减为指向其第一个元素的指针,而通过引用传递的数组不能用于初始化数组数据成员。

这意味着array<T, N>只能由另一个array<T, N>或由{}封闭列表中类型为T的多达N值的文字序列初始化。

从C++17开始,您将能够使用库函数to_array将对数组的引用转换为std::array:

std::array<int, 5>  arr{std::experimental::to_array(*reinterpret_cast<int (*)[5]>(&a))};

正如您在可能的实现部分中所看到的,这是在内部分别初始化每个元素:

return { {a[I]...} };

来自cpp数组引用:

数组类是聚合类型,因此没有自定义构造函数。

您应该使用默认初始化来构建它,然后将矢量内容复制到它。