这个问题来自于一个 API 的问题,该 API 需要
std::vector<uint8_t>
,但通常从 std::string
s 获取数据。
这就是我试图将
std::move
中的数据 std::string
转换为 std::vector<uint8_t>
:
string data = "Some data";
vector<uint8_t> utf8( std::make_move_iterator( data.begin() ),
std::make_move_iterator( data.end() ) );
// Above doesn't actually move the data,
puts( data.c_str() ); // Not empty!
有任何线索如何做到这一点吗?
从技术上讲,是的,但几乎绝对不是你想要的。您可以将
std::move
应用于 .data()
的结果,但是该结果并不引用向量的实际数据 - 它只是指向向量开头的指针。移动该指针意味着移动指针的值,而不是它引用的数据。
您实际上问的是您是否能够做到 - 是否可以简单地移动支持向量的内存并开始将其视为字符串 - 是不可能的。至少标准没有明确规定。在 C++23 中,
std::basic_string
获得了从任意范围构造字符串的能力,并且如果移动的向量作为范围传递(假设它可以 - 请参阅如下),但你不能明确要求。
这样做的原因是因为
std::vector
和 std::string
具有不同的不变量。乍一看,它们看起来很相似,它们都引用一个固定的内存缓冲区,其中只有该内存的一个子集实际上具有初始化值。然而,更深入地观察,std::string
还有一个需要保留的额外不变量 - 如果您在字符串上调用.data()
,第一个size()
元素将与向量相同,但最后一个之后的元素也相同 必须是空终止符 (0)。如果您要将一个完全充满元素的向量传递给一个字符串,那么该字符串将无法在末尾放置该空终止符,因为没有任何空间。这意味着从向量到字符串执行 std::move
意味着字符串 可能仍会分配内存,这与您在查看代码时可能期望的完全相反。
还有一个问题是,
std::string
和std::vector
的工作方式在管理内存方面甚至可能不一样。在转向 SSO 字符串之前,GCC 的 std::string
实现会在其分配的内存中分配大小和引用计数,然后每当您复制字符串时,它都会增加引用计数,但否则会指向相同的内存,并且仅如果您实际修改了字符串,则分配新的内存。在这种情况下,由 std::string
和 std::vector
分配的内存的内存布局将完全不兼容,因为 std::vector
会使它的字符从分配的内存的开头开始,但 std::string
会让它们从分配的内存开始之后一小段距离,超出了尺寸和引用计数。