std::array или std::vector из указателя

У меня есть массив данных в массиве C++/CLI, который я могу передать в собственную функцию, используя pin_ptr<T>, пока без проблем. Однако теперь мне нужно передать массив функции C++/STL, которая ожидает контейнер, такой как std::array или std::vector.

Самый простой способ сделать это (который я сделал первым) — скопировать элемент за элементом.

Второй самый простой способ — вызвать std::copy(), см. ответ на этот вопрос: convert System::array to std ::вектор.

Однако я хочу пропустить весь шаг копирования и вместо этого просто использовать указатель. Поскольку std::array требует аргумента шаблона для определения его длины, я не могу создать его во время выполнения (но, пожалуйста, поправьте меня, если я ошибаюсь). Есть ли способ создать вектор или другой тип контейнера STL без ненужного копирования данных?


person Community    schedule 11.11.2015    source источник
comment
Это может сработать, но если предположить, что C++/CLI имеет System::Array, это может быть не так чисто. Кроме того, у вас есть только основные типы? Нет строк или ref class, которые нужно преобразовать? Любой из этих случаев также исключает передачу указателей.   -  person crashmstr    schedule 11.11.2015
comment
Стандарт C++20 теперь поддерживает span, что должно позволить обернуть System::array.   -  person doug    schedule 30.06.2020
comment
@doug Это отличный момент, достойный ответа ИМХО!   -  person underscore_d    schedule 01.07.2020
comment
Для указателей, которые еще не принадлежат/не управляются кем-либо еще, другим способом сделать это будет использование unique_ptr или shared_ptr, которые могут владеть/ссылаться на динамические массивы. Тот факт, что они поставляются с собственной семантикой, означает, что они не будут работать во всех ситуациях, как и тот факт, что они не несут с собой размер, но они могут работать в некоторых ситуациях. Они, конечно, избегают копирования, так как они просто получают право собственности/ссылку на существующий указатель. Я не знаю, насколько часто они практически полезны/рекомендуются, но я упоминаю для завершения.   -  person underscore_d    schedule 01.07.2020
comment
@underscore_d. Это немного косвенно, поскольку std::span недоступен в С++ 17, и это последняя версия С++, которая поддерживает С++/cli. Но его можно поместить в отдельный файл, скомпилированный с последними и совместимыми ABI. Похоже, что С++ 17 может стать концом пути для С++/cli.   -  person doug    schedule 05.07.2020


Ответы (3)


Нет, без копирования не обойтись, во всяком случае, не со стандартными контейнерами.

Если вы все еще в порядке с копированием, вам следует взглянуть на конструктор std::vector потому что я думаю, что проще всего было бы сделать, например

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';
    }
}

Ожидаемый результат от программы:

From array    : 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
From container: 0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 
person Some programmer dude    schedule 11.11.2015

Начиная с С++ 20, есть способ использовать контейнеры С++ с управляемыми массивами в С++/cli, который позволяет избежать копирования данных: std::span

В С++ 20 можно использовать std::span для переноса управляемого массива С++/cli. Затем его можно использовать со стандартными алгоритмами контейнера.

К сожалению, Microsoft не поддерживает C++/cli выше C++17. Следовательно, нужно сначала передать указатель и длину функции в другом исходном файле, скомпилированном с использованием последней версии С++, при компиляции исходного файла вызывающей стороны с использованием более ранней версии С++ 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());
}
person doug    schedule 05.07.2020

Поскольку 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);
}
person doug    schedule 11.11.2015
comment
Соответствует ли этот стандарт? - person ofo; 09.08.2019
comment
@ofo Нет, поскольку по адресу, который был преобразован, не существует std::array, это поведение undefined. Вместо этого следует использовать .data(), чтобы законным образом получить указатель на инкапсулированный массив и передать его. - person underscore_d; 30.06.2020
comment
@underscore_d Согласен. Это взлом. Хотя это работает на компиляторах, которые я использую, как и другие хаки, которые являются UB в стандарте, лучше всего избегать этих вещей или изолировать их в каком-то файле, зарезервированном для старых вещей, которые требуют слишком много работы для рефакторинга. Кроме того, новый С++ 20 std:span решит проблему OP, если у них есть компилятор, который его поддерживает. Это позволит избежать копирования и поддерживает большинство операций с контейнерами. - person doug; 30.06.2020