어느 시점에서 정수 및 크기 배열에 대한 원시 포인터를 제공하는 외부 라이브러리를 사용하고 있습니다.
이제 std::vector
원시 포인터로 값을 액세스하지 않고 해당 값에 액세스하고 수정하는 데 사용하고 싶습니다 .
요점을 설명하는 명확한 예는 다음과 같습니다.
size_t size = 0;
int * data = get_data_from_library(size); // raw data from library {5,3,2,1,4}, size gets filled in
std::vector<int> v = ????; // pseudo vector to be used to access the raw data
std::sort(v.begin(), v.end()); // sort raw data in place
for (int i = 0; i < 5; i++)
{
std::cout << data[i] << "\n"; // display sorted raw data
}
예상 출력 :
1
2
3
4
5
그 이유는 <algorithm>
해당 데이터에 알고리즘 (정렬, 스왑 요소 등) 을 적용해야하기 때문입니다 .
그 벡터의 크기를 변경 반면이 변경되지 않을 것이다에, 그래서 push_back
, erase
, insert
그 벡터에 대한 작업에 필요하지 않습니다.
라이브러리의 데이터를 기반으로 벡터를 구성하고 해당 벡터를 수정하고 데이터를 라이브러리에 다시 복사하는 것이 가능하지만 데이터 세트가 실제로 클 수 있으므로 피하고 싶은 완전한 사본 두 개입니다.
답변
문제는 std::vector
배열에 포함 된 개체의 소유권이 있으므로 초기화하는 배열에서 요소의 복사본을 만들어야한다는 것입니다.
이를 피하기 위해 배열에 슬라이스 객체를 사용할 수 있습니다 (예 :와 비슷 std::string_view
함 std::string
). array_view
배열의 첫 번째 요소와 배열 길이에 대한 원시 포인터를 가져 와서 인스턴스가 구성된 고유 한 클래스 템플릿 구현을 작성할 수 있습니다 .
#include <cstdint>
template<typename T>
class array_view {
T* ptr_;
std::size_t len_;
public:
array_view(T* ptr, std::size_t len) noexcept: ptr_{ptr}, len_{len} {}
T& operator[](int i) noexcept { return ptr_[i]; }
T const& operator[](int i) const noexcept { return ptr_[i]; }
auto size() const noexcept { return len_; }
auto begin() noexcept { return ptr_; }
auto end() noexcept { return ptr_ + len_; }
};
array_view
배열을 저장하지 않습니다. 그것은 단지 배열의 시작과 그 배열의 길이에 대한 포인터를 가지고 있습니다. 따라서 array_view
객체를 구성하고 복사하는 것이 저렴합니다.
이후 array_view
제공 begin()
및 end()
회원 기능을, 당신은 표준 라이브러리 알고리즘 (예를 들어, 사용할 수있는 std::sort
, std::find
, std::lower_bound
, 등) 그것에를 :
#define LEN 5
auto main() -> int {
int arr[LEN] = {4, 5, 1, 2, 3};
array_view<int> av(arr, LEN);
std::sort(av.begin(), av.end());
for (auto const& val: av)
std::cout << val << ' ';
std::cout << '\n';
}
산출:
1 2 3 4 5
대신 std::span
(또는 gsl::span
)를 사용하십시오.
위의 구현은 슬라이스 객체 의 개념을 드러냅니다 . 그러나 C ++ 20부터는 std::span
대신 직접 사용할 수 있습니다 . 어쨌든 gsl::span
C ++ 14부터 사용할 수 있습니다 .
답변
C ++ 20 std::span
C ++ 20을 사용할 수 있다면 std::span
, 사용자에게 연속 된 요소 시퀀스를 보여주는 포인터-길이 쌍을 사용할 수 있습니다 . 그것은 일종의이며 std::string_view
, 모두 상태 std::span
및 std::string_view
비 소유 전망이며, std::string_view
읽기 전용이다.
문서에서 :
클래스 템플릿 범위는 위치 0에서 시퀀스의 첫 번째 요소가있는 연속 된 객체 시퀀스를 참조 할 수있는 객체를 설명합니다. 스팬은 정적 범위를 가질 수 있으며,이 경우 시퀀스의 요소 수는 유형으로 알려지고 인코딩됩니다.
따라서 다음과 같이 작동합니다.
#include <span>
#include <iostream>
#include <algorithm>
int main() {
int data[] = { 5, 3, 2, 1, 4 };
std::span<int> s{data, 5};
std::sort(s.begin(), s.end());
for (auto const i : s) {
std::cout << i << "\n";
}
return 0;
}
실시간 확인
때문에 std::span
기본적으로 포인터 – 길이 쌍, 당신은 너무 다음과 같은 방식으로 사용할 수 있습니다 :
size_t size = 0;
int *data = get_data_from_library(size);
std::span<int> s{data, size};
참고 : 모든 컴파일러가 지원하지는 않습니다 std::span
. 여기에서 컴파일러 지원을 확인 하십시오 .
최신 정보
C ++ 20을 사용할 수 없으면 gsl::span
기본적으로 C ++ 표준의 기본 버전 인을 사용할 수 있습니다 std::span
.
C ++ 11 솔루션
C ++ 11 표준으로 제한되어 있다면 자신 만의 간단한 span
클래스를 구현해 볼 수 있습니다 .
template<typename T>
class span {
T* ptr_;
std::size_t len_;
public:
span(T* ptr, std::size_t len) noexcept
: ptr_{ptr}, len_{len}
{}
T& operator[](int i) noexcept {
return *ptr_[i];
}
T const& operator[](int i) const noexcept {
return *ptr_[i];
}
std::size_t size() const noexcept {
return len_;
}
T* begin() noexcept {
return ptr_;
}
T* end() noexcept {
return ptr_ + len_;
}
};
C ++ 11 버전 라이브 확인
답변
알고리즘 라이브러리는 반복자와 함께 작동하므로 배열을 유지할 수 있습니다.
포인터 및 알려진 배열 길이
여기에서 raw 포인터를 반복자로 사용할 수 있습니다. 그들은 반복자가 지원하는 모든 작업을 지원합니다 (증가, 평등 비교, 값 등).
#include <iostream>
#include <algorithm>
int *get_data_from_library(int &size) {
static int data[] = {5,3,2,1,4};
size = 5;
return data;
}
int main()
{
int size;
int *data = get_data_from_library(size);
std::sort(data, data + size);
for (int i = 0; i < size; i++)
{
std::cout << data[i] << "\n";
}
}
data
리턴 한 반복자 같은 배열 dirst 부재 포인트 begin()
와 data + size
의해 반환 반복자 같은 배열의 마지막 요소 이후에 소자 포인트 end()
.
배열
여기에 사용할 수있는 std::begin()
및std::end()
#include <iostream>
#include <algorithm>
int main()
{
int data[] = {5,3,2,1,4}; // raw data from library
std::sort(std::begin(data), std::end(data)); // sort raw data in place
for (int i = 0; i < 5; i++)
{
std::cout << data[i] << "\n"; // display sorted raw data
}
}
그러나 data
포인터로 쇠퇴하지 않으면 길이 정보가 없어지기 때문에 작동합니다 .
답변
원시 배열에서 반복자를 가져 와서 알고리즘에서 사용할 수 있습니다.
int data[] = {5,3,2,1,4};
std::sort(std::begin(data), std::end(data));
for (auto i : data) {
std::cout << i << std::endl;
}
원시 포인터 (ptr + size)로 작업하는 경우 다음 기술을 사용할 수 있습니다.
size_t size = 0;
int * data = get_data_from_library(size);
auto b = data;
auto e = b + size;
std::sort(b, e);
for (auto it = b; it != e; ++it) {
cout << *it << endl;
}
UPD :
그러나 위의 예는 디자인이 잘못되었습니다. 라이브러리는 우리에게 원시 포인터를 반환하고 우리는 기본 버퍼가 할당 된 위치와 누가 버퍼를 해제해야하는지 모른다.
일반적으로 호출자는 함수가 데이터를 채우도록 버퍼링을 제공합니다. 이 경우 벡터를 미리 할당하고 기본 버퍼를 사용할 수 있습니다.
std::vector<int> v;
v.resize(256); // allocate a buffer for 256 integers
size_t size = get_data_from_library(v.data(), v.size());
// shrink down to actual data. Note that no memory realocations or copy is done here.
v.resize(size);
std::sort(v.begin(), v.end());
for (auto i : v) {
cout << i << endl;
}
C ++ 11 이상을 사용하는 경우 get_data_from_library ()를 만들어 벡터를 반환 할 수도 있습니다. 이동 작업 덕분에 메모리 사본이 없습니다.
답변
std::vector
복사본을 만들지 않고 는이 작업을 수행 할 수 없습니다 . std::vector
후드 아래에있는 포인터를 소유하고 제공된 할당자를 통해 공간을 할당합니다.
C ++ 20을 지원하는 컴파일러가 필요하다면 정확히이 목적으로 만들어진 std :: span 을 사용할 수 있습니다 . 포인터와 크기를 C ++ 컨테이너 인터페이스가있는 “컨테이너”로 래핑합니다.
그렇지 않은 경우 표준 버전의 기반 이 되는 gsl :: span 을 사용할 수 있습니다 .
다른 라이브러리를 가져 오지 않으려면 원하는 모든 기능에 따라이를 직접 구현할 수 있습니다.
답변
이제 std :: vector를 사용하여 이러한 값에 액세스하고 수정하고 싶습니다.
당신은 할 수 없습니다. 그게 아닙니다 std::vector
. std::vector
할당 자로부터 항상 획득되는 자체 버퍼를 관리합니다. 다른 버퍼의 소유권을 가지지 않습니다 (같은 유형의 다른 벡터는 제외).
반면에, 당신은 또한 필요가 없습니다 …
그 이유는 해당 데이터에 알고리즘 (정렬, 스왑 요소 등)을 적용해야하기 때문입니다.
이러한 알고리즘은 반복자에서 작동합니다. 포인터는 배열의 반복자입니다. 벡터가 필요하지 않습니다.
std::sort(data, data + size);
의 함수 템플릿과 달리 <algorithm>
range-for, std::begin
/ std::end
및 C ++ 20 범위와 같은 일부 도구 는 한 쌍의 반복자와 함께 작동하지 않지만 벡터와 같은 컨테이너에서는 작동합니다. 범위로 작동하는 반복자 + 크기에 대한 랩퍼 클래스를 작성할 수 있으며 이러한 도구와 함께 작동합니다. C ++ 20은 이러한 래퍼를 표준 라이브러리에 도입 할 것 std::span
입니다.
답변
에 대한 다른 좋은 제안 게다가 std::span
오는 C ++ (20) 과 gsl:span
자신 (경량)를 포함하여, span
다음 이미 (복사 주시기) 충분히 쉽게 때까지 클래스를 :
template<class T>
struct span {
T* first;
size_t length;
span(T* first_, size_t length_) : first(first_), length(length_) {};
using value_type = std::remove_cv_t<T>;//primarily needed if used with templates
bool empty() const { return length == 0; }
auto begin() const { return first; }
auto end() const { return first + length; }
};
static_assert(_MSVC_LANG <= 201703L, "remember to switch to std::span");
특별 참고로 부스트 범위 라이브러리 또한 부스트 범위 는보다 일반적인 범위의 개념에 관심이 있다면이 : https://www.boost.org/doc/libs/1_60_0/libs/range/doc/html/range/reference /utilities/iterator_range.html .
범위 개념은 C ++ 20에도 제공됩니다.