当询问C中常见的未定义行为时,灵魂比我提到的严格别名规则更加开明.
他们在说什么?
P0137引入了函数模板, std::launder
并在有关联合,生命周期和指针的部分中对标准进行了许多更改.
这篇论文解决了什么问题?我必须注意哪些语言的变化?我们在做什么launder
?
什么情况下,reinterpret_cast
荷兰国际集团一char*
(或char[N]
)是不确定的行为,当它定义的行为?我应该用什么经验来回答这个问题?
正如我们从这个问题中学到的,以下是未定义的行为:
alignas(int) char data[sizeof(int)];
int *myInt = new (data) int; // OK
*myInt = 34; // OK
int i = *reinterpret_cast<int*>(data); // <== UB! have to use std::launder
Run Code Online (Sandbox Code Playgroud)
但是在什么时候我们可以reinterpret_cast
在一个char
数组上做一个并且它不是未定义的行为?以下是一些简单的例子:
不new
,只是reinterpret_cast
:
alignas(int) char data[sizeof(int)];
*reinterpret_cast<int*>(data) = 42; // is the first cast write UB?
int i = *reinterpret_cast<int*>(data); // how about a read?
*reinterpret_cast<int*>(data) = 4; // how about the second write?
int j …
Run Code Online (Sandbox Code Playgroud)c++ undefined-behavior language-lawyer reinterpret-cast c++17
当在实例emplace_back()
上调用时std::vector
,会在先前分配的存储中创建一个对象。这可以通过 Placement-new 轻松实现,它非常便携。但现在,我们需要访问嵌入的元素而不调用未定义的行为。
从这篇文章中 我了解到有两种方法可以做到这一点
使用placement-new返回的指针:
auto *elemPtr = new (bufferPtr) MyType();
或者,从 C++17 开始,std::launder所转换的指针bufferPtr
auto *elemPtr2 = std::launder(reinterpret_cast<MyType*>(bufferPtr));
第二种方法可以很容易地推广到这样的情况,即我们有很多对象放置在相邻的内存位置,如std::vector
. 但在 C++17 之前人们做了什么?一种解决方案是将placement-new 返回的指针存储在单独的动态数组中。虽然这当然是合法的,但我认为它并没有真正实现 std::vector [此外,单独存储我们已经知道的所有地址是一个疯狂的想法]。另一个解决方案是存储lastEmplacedElemPtr
inside std::vector
,并从中删除适当的整数 - 但由于我们实际上没有MyType
对象数组,这可能也是未定义的。事实上,此 cppreference 页面中的一个示例声称,如果我们有两个相同类型的指针比较相等,并且其中一个可以安全地取消引用,则取消引用另一个可能仍然是未定义的。
那么,在 C++17 之前有没有办法以可移植的方式实现 std::vector 呢?或者也许 std::launder 确实是 C++ 的一个关键部分,当涉及到新的放置时,自 C++98 以来就缺失了?
我知道这个问题表面上与SO上的许多其他问题相似,但据我所知,他们都没有解释如何合法地迭代由placement-new构造的对象。事实上,这一切都有点令人困惑。例如,示例形式的cppreference 文档中的 std::aligned_storage
注释
似乎表明 C++11 和 C++17 之间存在一些变化,并且简单的别名违规reinterpret_cast
在 C++17 之前是合法的 [无需std::launder
]。类似地,在文档 的示例中,std::malloc
他们只是对返回的指针进行指针算术 …
我正在阅读有关严格别名的内容,但它仍然有点模糊,我无法确定定义/未定义行为的界限.我发现最详细的帖子集中在C.所以如果你能告诉我这是否允许以及自C++ 98/11以来发生了什么变化,那将是很好的...
#include <iostream>
#include <cstring>
template <typename T> T transform(T t);
struct my_buffer {
char data[128];
unsigned pos;
my_buffer() : pos(0) {}
void rewind() { pos = 0; }
template <typename T> void push_via_pointer_cast(const T& t) {
*reinterpret_cast<T*>(&data[pos]) = transform(t);
pos += sizeof(T);
}
template <typename T> void pop_via_pointer_cast(T& t) {
t = transform( *reinterpret_cast<T*>(&data[pos]) );
pos += sizeof(T);
}
};
// actually do some real transformation here (and actually also needs an inverse)
// ie this …
Run Code Online (Sandbox Code Playgroud) 我仍在努力理解严格别名允许和不允许的内容。这个具体的例子是否违反了严格的别名规则?如果不是,为什么?是因为我将新的不同类型放入 char* 缓冲区吗?
template <typename T>
struct Foo
{
struct ControlBlock { unsigned long long numReferences; };
Foo()
{
char* buffer = new char[sizeof(T) + sizeof(ControlBlock)];
// Construct control block
new (buffer) ControlBlock{};
// Construct the T after the control block
this->ptr = buffer + sizeof(ControlBlock);
new (this->ptr) T{};
}
char* ptr;
T* get() {
// Here I cast the char* to T*.
// Is this OK because T* can alias char* or because
// I placement newed a T …
Run Code Online (Sandbox Code Playgroud) 以下示例来自cppreference.com 的std::aligned_storage 页面:
#include <iostream>
#include <type_traits>
#include <string>
template<class T, std::size_t N>
class static_vector
{
// properly aligned uninitialized storage for N T's
typename std::aligned_storage<sizeof(T), alignof(T)>::type data[N];
std::size_t m_size = 0;
public:
// Create an object in aligned storage
template<typename ...Args> void emplace_back(Args&&... args)
{
if( m_size >= N ) // possible error handling
throw std::bad_alloc{};
new(data+m_size) T(std::forward<Args>(args)...);
++m_size;
}
// Access an object in aligned storage
const T& operator[](std::size_t pos) const
{
return *reinterpret_cast<const T*>(data+pos);
} …
Run Code Online (Sandbox Code Playgroud) 我正在读关于reinterpret_cast及其别名规则的说明(http://en.cppreference.com/w/cpp/language/reinterpret_cast).
我写了那段代码:
struct A
{
int t;
};
char *buf = new char[sizeof(A)];
A *ptr = reinterpret_cast<A*>(buf);
ptr->t = 1;
A *ptr2 = reinterpret_cast<A*>(buf);
cout << ptr2->t;
Run Code Online (Sandbox Code Playgroud)
我认为这些规则不适用于此:
在我看来,这段代码是不正确的.我对吗?代码是否正确?
另一方面,连接功能(man 2 connect)和struct sockaddr怎么样?
int connect(int sockfd, const struct sockaddr *addr,
socklen_t addrlen);
Run Code Online (Sandbox Code Playgroud)
例如.我们有struct sockaddr_in,我们必须将它转换为struct sockaddr.以上规则也不适用,这个演员是不正确的?