【发布时间】:2019-03-30 11:56:57
【问题描述】:
这可能会让一些编码人员感到惊讶,而且,如果没有编译器的非标准支持,就不可能实现std::vector。问题本质上在于对原始存储区域执行指针运算的能力。出现在@ShafikYaghmour 答案中的论文p0593: Implicit creation of objects for low-level object manipulation 清楚地揭示了存在的问题并建议修改标准,以便更容易实现容器等向量和其他法律级别的编程技术。
尽管如此,我想知道是否没有解决方法来实现与 std::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
我的想法是使用一个从不初始化其成员的联合。
//almost trivialy default constructible
template<class T>
union atdc{
char _c;
T value;
atdc ()noexcept{ }
~atdc(){}
};
原始存储将使用此联合类型的数组进行初始化,并且始终在此数组上执行指针运算。然后在每个 push_back 的联合的非活动成员上构造元素。
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);
上面这段代码中是否有任何未定义的行为?
【问题讨论】:
-
什么是法律级编程???
-
@YunfeiChen 在 C++ 中,几乎只使用语言原语。高级编程是你只使用详细的抽象库
标签: c++ memory-management language-lawyer undefined-behavior