当我使用g ++编译这个示例代码时,我收到此警告:
警告:解除引用类型惩罚指针将破坏严格别名规则
[-Wstrict-aliasing]
代码:
#include <iostream>
int main()
{
alignas(int) char data[sizeof(int)];
int *myInt = new (data) int;
*myInt = 34;
std::cout << *reinterpret_cast<int*>(data);
}
Run Code Online (Sandbox Code Playgroud)
在这种情况下,不是data别名int,因此将其强制转换为int不会违反严格的别名规则?或者我在这里遗漏了什么?
编辑:奇怪,当我这样定义时data:
alignas(int) char* data = new char[sizeof(int)];
Run Code Online (Sandbox Code Playgroud)
编译器警告消失了.堆栈分配是否与严格别名产生差异?事实上它是一个char[]而不是一个char*意味着它实际上不能为任何类型别名吗?
我正在写一个类似于类型擦除的函数包装器std::function.(是的,我已经看过类似的实现,甚至是p0288r0提案,但我的用例非常狭窄且有些专业化.).以下大量简化的代码说明了我当前的实现:
class Func{
alignas(sizeof(void*)) char c[64]; //align to word boundary
struct base{
virtual void operator()() = 0;
virtual ~base(){}
};
template<typename T> struct derived : public base{
derived(T&& t) : callable(std::move(t)) {}
void operator()() override{ callable(); }
T callable;
};
public:
Func() = delete;
Func(const Func&) = delete;
template<typename F> //SFINAE constraints skipped for brevity
Func(F&& f){
static_assert(sizeof(derived<F>) <= sizeof(c), "");
new(c) derived<F>(std::forward<F>(f));
}
void operator () (){
return reinterpret_cast<base*>(c)->operator()(); //Warning
}
~Func(){
reinterpret_cast<base*>(c)->~base(); //Warning
} …Run Code Online (Sandbox Code Playgroud) 当在实例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 [此外,单独存储我们已经知道的所有地址是一个疯狂的想法]。另一个解决方案是存储lastEmplacedElemPtrinside 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
他们只是对返回的指针进行指针算术 …
我最近看到像这样的类用于"按需"构造对象,而不必出于各种原因使用动态内存分配.
#include <cassert>
template<typename T>
class StaticObject
{
public:
StaticObject() : constructed_(false)
{
}
~StaticObject()
{
if (constructed_)
((T*)object_)->~T();
}
void construct()
{
assert(!constructed_);
new ((T*)object_) T;
constructed_ = true;
}
T& operator*()
{
assert(constructed_);
return *((T*)object_);
}
const T& operator*() const
{
assert(constructed_);
return *((T*)object_);
}
private:
bool constructed_;
alignas(alignof(T)) char object_[sizeof(T)];
};
Run Code Online (Sandbox Code Playgroud)
这段代码,即将正确对齐的char数组转换为对象指针,被C++ 14标准视为未定义的行为,还是完全没问题?