来自指针的 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::arraystd::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_ptrshared_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 成员的指针

当对象很大时返回一个指针或值

编译器生成的默认构造函数是否会将std :: array中的指针初始化为nullptr?

使用来自 boost::array 与 std::array 的 std::string::assign 不兼容

为啥我不能在 std::array< int, 3 > 的指针上使用运算符 [] 来索引值?

是否可以在恒定时间内交换 std::array ?