c++ - 实现一个没有未定义行为的类似 std::vector 的容器

标签 c++ memory-management language-lawyer undefined-behavior

这可能会让一些编码人员感到惊讶,而且,尽管可能会令人惊讶,但如果没有编译器的非标准支持,就不可能实现 std::vector。问题本质上在于对原始存储区域执行指针运算的能力。论文,p0593: Implicit creation of objects for low-level object manipulation ,出现在@ShafikYaghmour 的回答中,清楚地暴露了问题并建议修改标准,以便更容易地实现容器之类的 vector 和其他法律级别的编程技术。

尽管如此,我想知道是否没有解决方法来实现与 std::vector 等效的类型,仅使用语言提供的内容而不使用任何标准库。

目标是在原始存储区域中一个接一个地构建 vector 元素,并能够使用迭代器访问这些元素。这相当于 std::vector 上的 push_back 序列。

要了解问题,请简化在 libc++ 或 libstdc++ 中执行 std::vector 的操作:

void access_value(std::string x);

std::string s1, s2, s3;
//allocation
auto p=static_cast<std::string*>(::operator new(10*sizeof(std::string)));

//push_back s1
new(p) std::string(s1);
access_value(*p);//undefined behavior, p is not a pointer to object

//push_back s2
new(p+1) std::string(s2);//undefined behavior
        //, pointer arithmetic but no array (neither implicit array of size 1)
access_value(*(p+1));//undefined behavior, p+1 is not a pointer to object

//push_back s2
new(p+2) std::string(s3);//undefined behavior
        //, pointer arithmetic but no array
access_value(*(p+2));//undefined behavior, p+2 is not a pointer to object

我的想法是使用一个从不初始化其成员的 union 。

//almost trivialy default constructible
template<class T>
union atdc{
  char _c;
  T value;
  atdc ()noexcept{ }
  ~atdc(){}
};

原始存储将用这个 union 类型的数组初始化,指针运算总是在这个数组上进行。然后在每个 push_back 时在 union 体的非事件成员上构造元素。

std::string s1, s2, s3;
auto p=::operator new(10*sizeof(std::string));
auto arr = new(p) atdc<std::string>[10];
//pointer arithmetic on arr is allowed

//push_back s1
new(&arr[0].value) std::string(s1); //union member activation
access_value(arr[0].value);

//push_back s2
new(&arr[1].value) std::string(s2);
access_value(arr[1].value);

//push_back s2
new(&arr[2].value) std::string(s2);
access_value(arr[2].value);

上面这段代码有没有未定义的行为?

最佳答案

这是一个正在积极讨论的话题,我们可以在提案中看到这个p0593: Implicit creation of objects for low-level object manipulation .这是对这些问题的非常扎实的讨论,以及为什么如果不进行更改就无法解决这些问题。如果您有不同的方法或对正在考虑的方法有强烈的看法,您可能希望联系提案作者。

它包括这个讨论:

2.3. Dynamic construction of arrays

Consider this program that attempts to implement a type like std::vector (with many details omitted for brevity):

....

In practice, this code works across a range of existing implementations, but according to the C++ object model, undefined behavior occurs at points #a, #b, #c, #d, and #e, because they attempt to perform pointer arithmetic on a region of allocated storage that does not contain an array object.

At locations #b, #c, and #d, the arithmetic is performed on a char*, and at locations #a, #e, and #f, the arithmetic is performed on a T*. Ideally, a solution to this problem would imbue both calculations with defined behavior.

  1. Approach

The above snippets have a common theme: they attempt to use objects that they never created. Indeed, there is a family of types for which programmers assume they do not need to explicitly create objects. We propose to identify these types, and carefully carve out rules that remove the need to explicitly create such objects, by instead creating them implicitly.

使用 adc union 的方法有一个问题,即我们希望能够通过指针 T* 访问包含的数据,即通过 std::vector::data .作为 T* 访问 union 将违反 strict aliasing rules因此是未定义的行为。

关于c++ - 实现一个没有未定义行为的类似 std::vector 的容器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52996590/

相关文章:

c++ - 如何将 C++ 类/结构转换为原始/不同类型/类/结构?

c++ - 没有 O(1) 操作来连接来自两个 forward_lists 的元素?

c++ - 表单输入验证,多焦点问题

c# - 内存消耗 .Net 应用程序(Azure 网站)

c - 使用 realloc 与 free -> malloc 函数的区别

c++ - 当我的 vector< vector< vector<int>>> 大于 RAM 时,为什么我的程序会产生正确的输出?

c++ - 是什么让 union 成员(member)活跃起来?

c++ - 函数风格的异常重新抛出。标准/编译器中的错误?

C++ 我应该使用模板,我即将创建一个词法分析器,为什么它应该是有限的字符?

c++ - 为什么 `promise_type::await_transform`是贪婪的?