[英]Why can't we change data pointer of std::vector?
I have an array char* source
and a vector std::vector<char> target
.我有一个数组
char* source
和一个向量std::vector<char> target
。 I'd like to make the vector target
point to source
in O(1), without copying the data.我想让向量
target
指向 O(1) 中的source
,而不复制数据。
Something along these lines:这些方面的东西:
#include <vector>
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.setData(source); // <- Doesn't exist
// OR
std::swap(target.data(), source); // <- swap() does not support char*
delete[] source;
Why is it not possible to manually change where a vector points to?为什么不能手动更改向量指向的位置? Is there some specific, unmanageable problem that would arise if this was possible?
如果可能的话,是否会出现一些特定的、无法管理的问题?
C++ vector
class supports adding and deleting elements, with guaranteed consecutive order in memory. C++
vector
class支持添加和删除元素,保证memory中的连续顺序。 If you could initialize your vector
with existing memory buffer, and add enough elements to it, it would either overflow or require reallocation.如果您可以使用现有的 memory 缓冲区初始化您的
vector
,并向其中添加足够的元素,它会溢出或需要重新分配。
The interface of vector
assumes that it manages its internal buffer, that is, it can allocate, deallocate, resize it whenever it wants (within spec, of course). vector
的接口假定它管理其内部缓冲区,也就是说,它可以随时分配、解除分配、调整大小(当然,在规范范围内)。 If you need something that is not allowed to manage its buffer, you cannot use vector
- use a different data structure or write one yourself.如果您需要一些不允许管理其缓冲区的东西,则不能使用
vector
- 使用不同的数据结构或自己编写一个。
You can create a vector
object by copying your data (using a constructor with two pointers or assign
), but this is obviously not what you want.您可以通过复制数据(使用带有两个指针或
assign
的构造函数)来创建vector
object,但这显然不是您想要的。
Alternatively, you can use string_view
, which looks almost or maybe exactly what you need.或者,您可以使用
string_view
,它看起来几乎或可能正是您需要的。
std::vector
is considered to be the owner of the underlying buffer. std::vector
被认为是底层缓冲区的所有者。 You can change the buffer but this change causes allocation ie making a copy of the source buffer which you don't want (as stated in the question).您可以更改缓冲区,但此更改会导致分配,即制作您不想要的源缓冲区的副本(如问题中所述)。
You could do the following:您可以执行以下操作:
#include <vector>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::vector<char> target;
target.resize(3);
target.assign(source, source + 3);
delete[] source;
return 0;
}
but againstd::vector::assign
:但又
std::vector::assign
:
Replaces the contents with copies of those in the range [first, last).
将内容替换为 [first, last) 范围内的内容的副本。
So copy is performed again.因此再次执行复制。 You can't get away from it while using
std::vector
.使用
std::vector
时您无法摆脱它。
If you don't want to copy data, then you should use std::span
from C++20 (or create your own span) or use std::string_view
(which looks suitable for you since you have an array of char
s).如果您不想复制数据,那么您应该使用 C++20 中的
std::span
(或创建您自己的跨度)或使用std::string_view
(看起来适合您,因为您有一个char
数组)。
1st option: Using std::string_view
第一个选项:使用
std::string_view
Since you are limited to C++17, std::string_view
might be perfect for you.由于您仅限于 C++17,因此
std::string_view
可能非常适合您。 It constructs a view of the first 3 characters of the character array starting with the element pointed by source
.它从
source
指向的元素开始构造字符数组的前 3 个字符的视图。
#include <iostream>
#include <string_view>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::string_view strv( source, 3 );
delete[] source;
return 0;
}
2nd option: Using std::span
from C++20第二个选项:使用 C++20 中的
std::span
std::span
comes from C++20 so it might not be the most perfect way for you, but you might be interested in what it is and how it works. std::span
来自 C++20,因此它可能不是最适合您的方式,但您可能会对它是什么以及它的工作原理感兴趣。 You can think of std::span
as a bit generalized version of std::string_view
because it is a contiguous sequence of objects of any type, not just characters.您可以将
std::span
视为std::string_view
的一个通用版本,因为它是任何类型的对象的连续序列,而不仅仅是字符。 The usage is similar as with the std::string_view
:用法与
std::string_view
:
#include <span>
#include <iostream>
int main() {
char* source = new char[3] { 1, 2, 3 };
std::span s( source, 3 );
delete[] source;
return 0;
}
3rd option: Your own span第三个选项:你自己的跨度
If you are limited to C++17, you can think of creating your own span
struct.如果您仅限于 C++17,您可以考虑创建自己的
span
结构。 It might still be an overkill but let me show you (btw take a look at this more elaborated answer ):这可能仍然是一种矫枉过正,但让我告诉你(顺便看看这个更详细的答案):
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_;
}
};
int main() {
char* source = new char[3] { 1, 2, 3 };
span s( source, 3 );
delete[] source;
return 0;
}
So the usage is the same as with the C++20's version of std::span
.所以用法与 C++20 版本的
std::span
相同。
The std::string_view and std::span are good things to have (if you have compiler version supporting them). std::string_view 和 std::span 是好东西(如果你有支持它们的编译器版本)。 Rolling your own similars is ok too.
滚动你自己的类似物也可以。
But some people miss the whole point why one would want to do this exactly to a vector:但是有些人忽略了为什么要对向量执行此操作的全部要点:
Struct[]
+ size_t
and give you ownershipStruct[]
+ size_t
并给你所有权std::vector<Struct>
std::vector<Struct>
的API ownership could be easily transferred into the vector and no copies made!所有权可以很容易地转移到向量中,并且没有复制!
You can say: But what about custom allocators, memory mapped file pointers, rom memory that I could then set as the pointer?你可以说:但是自定义分配器,memory 映射文件指针,ROM memory 然后我可以设置为指针呢?
Maybe give a warning on compiling this code yes, but it would be nice if it would be possible.也许会在编译此代码时发出警告,但如果可能的话,那就太好了。
I would do it this way:我会这样做:
std::vector
would get a constructor that asks for a std::vector::raw_source
std::vector
会得到一个构造函数,它要求std::vector::raw_source
std::vector::raw_source
is an uint8_t*, size_t, bool
struct (for now) std::vector::raw_source
是一个uint8_t*, size_t, bool
结构(目前)bool takeOwnership
: tells if we are taking ownership (false => copy) bool takeOwnership
: 告诉我们是否取得所有权 (false => copy)size_t size
: the size of the raw data size_t size
: 原始数据的大小uint8_t* ptr
: the pointer to raw data uint8_t* ptr
:指向原始数据的指针 Yes API I say look more complicated than a single "set_data(..)" or "own_memory(...)" function, but tried to make it clear that anyone who ever uses this api pretty much understands implications of it automagically.是的 API 我说看起来比单个“set_data(..)”或“own_memory(...)”function 更复杂,但试图澄清任何曾经使用过这个 Z8A5DA52ED12644427D359E70CA5 的人都非常了解它的含义。 I would like this API to exists, but maybe I still overlook other causes of issues?
我希望这个 API 存在,但也许我仍然忽略了其他问题原因?
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.