调整 C++ 的大小std::vector;不初始化数据

发布于 2024-12-08 11:20:56 字数 658 浏览 0 评论 0原文

对于向量,可以假设元素连续存储在内存中,从而允许将 [&vec[0], &vec[vec.capacity()) 范围用作普通数组。例如,

vector<char> buf;
buf.reserve(N);
int M = read(fd, &buf[0], N);

但现在向量不知道它包含由 read() 从外部添加的 M 字节数据。我知道vector::resize()设置了大小,但它也会清除数据,所以它不能用于在read()之后更新大小称呼。

有没有一种简单的方法可以将数据直接读入向量并随后更新大小?是的,我知道明显的解决方法,例如使用一个小数组作为临时读取缓冲区,并使用vector::insert()将其附加到向量的末尾:

char tmp[N];
int M = read(fd, tmp, N);
buf.insert(buf.end(), tmp, tmp + M)

这有效(这就是我今天正在做),但令我困扰的是,如果我可以将数据直接放入向量中,则不需要额外的复制操作。

那么,有没有一种简单的方法可以在外部添加数据时修改向量大小呢?

With vectors, one can assume that elements are stored contiguously in memory, allowing the range [&vec[0], &vec[vec.capacity()) to be used as a normal array. E.g.,

vector<char> buf;
buf.reserve(N);
int M = read(fd, &buf[0], N);

But now the vector doesn't know that it contains M bytes of data, added externally by read(). I know that vector::resize() sets the size, but it also clears the data, so it can't be used to update the size after the read() call.

Is there a trivial way to read data directly into vectors and update the size after? Yes, I know of the obvious workarounds like using a small array as a temporary read buffer, and using vector::insert() to append that to the end of the vector:

char tmp[N];
int M = read(fd, tmp, N);
buf.insert(buf.end(), tmp, tmp + M)

This works (and it's what I'm doing today), but it just bothers me that there is an extra copy operation there that would not be required if I could put the data directly into the vector.

So, is there a simple way to modify the vector size when data has been added externally?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

晨光如昨 2024-12-15 11:20:56
vector<char> buf;
buf.reserve(N);
int M = read(fd, &buf[0], N);

此代码片段调用未定义的行为。即使您已经预留了空间,您也不能写入超过 size() 元素的内容。即使您已预留空间

正确的代码是这样的:

vector<char> buf;
buf.resize(N);
int M = read(fd, &buf[0], N);
buf.resize(M);


PS. Your statement "With vectors, one can assume that elements are stored contiguously in memory, allowing the range [&vec[0], &vec[vec.capacity()) to be used as a normal array" isn't true. The allowable range is [&vec[0], &vec[vec.size()).

vector<char> buf;
buf.reserve(N);
int M = read(fd, &buf[0], N);

This code fragment invokes undefined behavior. You can't write beyond than size() elements, even if you have reserved the space.

The correct code is like:

vector<char> buf;
buf.resize(N);
int M = read(fd, &buf[0], N);
buf.resize(M);


PS. Your statement "With vectors, one can assume that elements are stored contiguously in memory, allowing the range [&vec[0], &vec[vec.capacity()) to be used as a normal array" isn't true. The allowable range is [&vec[0], &vec[vec.size()).

尝蛊 2024-12-15 11:20:56

另一个较新的问题(与此问题重复)有答案,看起来与此处所问的完全一样。这是它的(v3)副本以供快速参考:

初始化也无法关闭是一个已知问题
显式用于 std::vector

人们通常会实现自己的 pod_vector,但实际上并没有这样做
元素的任何初始化。

另一种方法是创建一个与 char 布局兼容的类型,
它的构造函数不执行任何操作:

struct NoInitChar
{
    字符值;
    NoInitChar() {
        // 什么都不做
        static_assert(sizeof *this == sizeof value, "无效大小");
        static_assert(__alignof *this == __alignof value, "无效对齐");
    }
};

int main() {
    std::vector v;
    v.调整大小(10); // 调用NoInitChar(),它不会初始化

    // 看吧,没有reinterpret_cast<>!
    char* beg = &v.front().value;
    char* end = beg + v.size();
}

Another, newer, question, a duplicate of this one, has an answer, which looks like exactly what is asked here. Here's its copy (of v3) for quick reference:

It is a known issue that initialization can not be turned off even
explicitly for std::vector.

People normally implement their own pod_vector<> that does not do
any initialization of the elements.

Another way is to create a type which is layout-compatible with char,
whose constructor does nothing:

struct NoInitChar
{
    char value;
    NoInitChar() {
        // do nothing
        static_assert(sizeof *this == sizeof value, "invalid size");
        static_assert(__alignof *this == __alignof value, "invalid alignment");
    }
};

int main() {
    std::vector<NoInitChar> v;
    v.resize(10); // calls NoInitChar() which does not initialize

    // Look ma, no reinterpret_cast<>!
    char* beg = &v.front().value;
    char* end = beg + v.size();
}
恬淡成诗 2024-12-15 11:20:56

看起来你可以在 C++11 中做你想做的事情(尽管我自己还没有尝试过)。您必须为向量定义一个自定义分配器,然后使用 emplace_back()

首先,定义

struct do_not_initialize_tag {};

然后使用此成员函数定义您的分配器:

class my_allocator {
    void construct(char* c, do_not_initialize_tag) const {
        // do nothing
    }

    // details omitted
    // ...
}

现在您可以将元素添加到数组而不初始化它们:

std::vector<char, my_allocator> buf;
buf.reserve(N);
for (int i = 0; i != N; ++i)
    buf.emplace_back(do_not_initialize_tag());
int M = read(fd, buf.data(), N);
buf.resize(M);

这的效率取决于编译器的优化器。例如,循环可以将 size 成员变量递增 N 次。

It looks like you can do what you want in C++11 (though I haven't tried this myself). You'll have to define a custom allocator for the vector, then use emplace_back().

First, define

struct do_not_initialize_tag {};

Then define your allocator with this member function:

class my_allocator {
    void construct(char* c, do_not_initialize_tag) const {
        // do nothing
    }

    // details omitted
    // ...
}

Now you can add elements to your array without initializing them:

std::vector<char, my_allocator> buf;
buf.reserve(N);
for (int i = 0; i != N; ++i)
    buf.emplace_back(do_not_initialize_tag());
int M = read(fd, buf.data(), N);
buf.resize(M);

The efficiency of this depends on the compiler's optimizer. For instance, the loop may increment the size member variable N times.

余厌 2024-12-15 11:20:56

您的程序片段已进入未定义行为的领域。

buf.empty() 为 true 时,buf[0] 具有未定义的行为,因此 &buf[0] 也是未定义的。

这个片段可能会做你想要的。

vector<char> buf;
buf.resize(N); // preallocate space
int M = read(fd, &buf[0], N);
buf.resize(M); // disallow access to the remainder

Your program fragment has entered the realm of undefined behavior.

when buf.empty() is true, buf[0] has undefined behavior, and therefore &buf[0] is also undefined.

This fragment probably does what you want.

vector<char> buf;
buf.resize(N); // preallocate space
int M = read(fd, &buf[0], N);
buf.resize(M); // disallow access to the remainder
沉鱼一梦 2024-12-15 11:20:56

写入第 size() 个元素及其之后是未定义的行为。

下一个示例以 C++ 方式将整个文件复制到向量中(不需要知道文件的大小,也不需要在向量中预分配内存):

#include <algorithm>
#include <fstream>
#include <iterator>
#include <vector>

int main()
{
    typedef std::istream_iterator<char> istream_iterator;
    std::ifstream file("example.txt");
    std::vector<char> input;

    file >> std::noskipws;
    std::copy( istream_iterator(file), 
               istream_iterator(),
               std::back_inserter(input));
}

Writing into and after the size()th element is an undefined behavior.

Next example copies whole file into a vector in a c++ way (no need to know the file's size and no need to preallocate the memory in the vector):

#include <algorithm>
#include <fstream>
#include <iterator>
#include <vector>

int main()
{
    typedef std::istream_iterator<char> istream_iterator;
    std::ifstream file("example.txt");
    std::vector<char> input;

    file >> std::noskipws;
    std::copy( istream_iterator(file), 
               istream_iterator(),
               std::back_inserter(input));
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文