来自指针的 std::array 或 std::vector
Posted
技术标签:
【中文标题】来自指针的 std::array 或 std::vector【英文标题】:std::array or std::vector from pointer 【发布时间】:2016-02-12 18:01:33 【问题描述】:我在 C++/CLI 数组中有一个数据数组,我可以使用 pin_ptr<T>
将其传递给本机函数,到目前为止没有问题。但是,现在我需要将数组传递给 C++/STL 函数,该函数需要一个容器,例如 std::array
或 std::vector
。
执行此操作的简单方法(我首先这样做)是逐个元素复制。
第二种最简单的方法是致电std::copy()
,请参阅此问题的答案:convert System::array to std::vector。
但是,我想跳过整个复制步骤,而只使用指针。看到std::array
需要一个模板参数来确定它的长度,我无法在运行时创建一个(但如果我错了,请纠正我)。有没有一种方法可以创建向量或不同类型的 STL 容器,而无需复制不必要的数据?
【问题讨论】:
它可能有效,但假设 C++/CLI 有一个 System::Array,它可能不是那么干净。另外,你只有基本类型吗?没有需要转换的字符串或ref class
?这些情况中的任何一种都会阻止传递指针。
C++20 标准现在支持span
,这应该允许包装System::array
。
@doug 说得很好,恕我直言,值得回答!
对于其他人尚未拥有/管理的指针,另一种方法是使用unique_ptr
或shared_ptr
,它们可以拥有/引用动态数组。那些带有语义的事实意味着它们不会适用于所有情况,它们不携带尺寸的事实也是如此,但它们可能适用于某些情况。他们当然避免复制,因为他们只是获取现有指针的所有权/引用。我不知道它们实际上有用/推荐的频率,但我提到完成。
@underscore_d。这有点间接,因为std::span
在 c++17 中不可用,这是支持 c++/cli 的最新版本的 c++。但它可以放在一个单独的文件中,用最新的编译器和 ABI 兼容。看起来 c++17 可能是 c++/cli 的尽头。
【参考方案1】:
从 C++20 开始,有一种方法可以在 c++/cli 中使用带有托管数组的 c++ 容器来避免复制数据:std::span
在 c++20 中,可以使用 std::span
来包装托管 c++/cli 数组。然后它可以与标准容器算法一起使用。
不幸的是,微软不支持 c++17 之后的 c++/cli。因此,必须首先将指针和长度传递给使用 c++latest 编译的不同源文件中的函数,同时使用较早的 c++17/cli 编译调用者的源文件。幸运的是,ABI 是兼容的。这很容易在 Visual Studio 2019 的属性页面中为每个文件设置。
这里有一些示例代码创建一个小的托管array<double>
,然后调用一个函数,该函数用std::span
包装托管数据,然后用std::sort
排序
// file1.cpp compile with /cli
#include <iostream>
using namespace System;
void sortme(double *p, int len);
int main()
array<double>^ v = gcnew array<double> 1.0, 3.0, 2.0, 4.0;
pin_ptr<double> pin=&v[0];
int len=v->Length;
sortme(pin, len);
for (int i = 0; i < len; i++)
std::cout << v[i] << "\n"; // prints sorted array
// file2.cpp compile with c++latest
#include <span>
#include <algorithm>
void sortme(double *p, int len)
std::span data_clr(p, len);
std::sort(data_clr.begin(), data_clr.end());
【讨论】:
【参考方案2】:由于 std::array 只是一个包装器,您可以将常规数组转换为指向 std::array 的指针。这当然不适用于其他容器。
#include <array>
#include <iostream>
void test(std::array<int, 10>* pia)
std::cout << (*pia)[0] << std::endl;
int main()
int ix[10] 0 ;
test((std::array<int, 10> *) ix);
【讨论】:
这个标准符合吗? @ofo 不,因为没有std::array
存在于被转换的地址,这是未定义的行为。应该改为使用.data()
以合法方式获取指向封装数组的指针,然后传递它。
@underscore_d 同意。这是一个黑客。虽然它适用于我使用的编译器,但就像标准中的其他 UB 黑客一样,最好避免这些事情,或者将它们隔离在为旧东西保留的一些文件中,这些文件需要重构太多工作。顺便说一句,如果他们有一个支持它的编译器,新的 C++20 std:span 将解决 OP 的问题。它将避免复制并支持大多数容器操作。【参考方案3】:
不,不复制是不可能的,无论如何都不能使用标准容器。
如果您仍然可以复制,那么您应该查看std::vector
constructor,因为我认为最简单的方法是例如
std::vector<T>(your_pointer, your_pointer + number_of_elements)
如果您确实想避免复制,那么围绕指针编写一个简单的包装器并不难,包括迭代所需的简单迭代器(我猜它必须是标准容器的原因)。
只是为了好玩,因为我有一些时间,所以我创建了这样一个包装器。它包括索引和迭代器。没有边界检查。
见https://gist.github.com/pileon/c21cfba496e6c352dd81
使用它的示例程序:
#include <iostream>
#include "pointer_container.h"
int main()
int a[20];
std::iota(a, a + 20, 0); // Initialize array
std::cout << "From array : ";
for (const auto item : a)
std::cout << item << ' ';
std::cout << '\n';
pointer_container<int> c(a, 20);
std::cout << "From container: ";
for (const auto item : c)
std::cout << item << ' ';
std::cout << '\n';
程序的预期输出:
从数组:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 从容器:0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19【讨论】:
以上是关于来自指针的 std::array 或 std::vector的主要内容,如果未能解决你的问题,请参考以下文章
编译器生成的默认构造函数是否会将std :: array中的指针初始化为nullptr?
使用来自 boost::array 与 std::array 的 std::string::assign 不兼容