考虑以下C ++ 14代码:
#include <cassert>
#include <new>
#include <type_traits>
struct NonStandardLayout
{
// ...
};
int main()
{
using T = NonStandardLayout;
std::aligned_storage_t< sizeof(T), alignof(T) > storage;
T *const valid_ptr = new(static_cast<void *>(&storage)) T;
T *const maybe_ptr = reinterpret_cast<T *>(&storage);
assert(maybe_ptr == valid_ptr); // ???
valid_ptr->T::~T();
return 0;
}
对于任何类型的T?
,标准是否保证示例中的断言永远不会失败?在查看最新标准(http://eel.is/c++draft/)时,我看不到任何对此特定情况的引用,但我发现以下段落可以指出答案“是”&#39;。
认为这是正确的
[expr.new/15]
和
[new.delete.placement / 2]
一起表示valid_ptr
的值将等于storage
的地址,总是?
如果是这样,reinterpret_cast
是否会产生指向完全构造对象的指针?因为,
[expr.reinterpret.cast / 7],
[expr.static.cast / 13]
和
[basic.compound / 4]
一起似乎表明应该是这样的。
根据我的观察,默认分配器的库实现似乎与此类似,并且无需担心!像这样真的安全吗?
我们如何确定两个指针是相同的,还是我们可以?
答案 0 :(得分:5)
我很惊讶没人提到这个便宜的反例:
struct foo {
static foo f;
// might seem dubious but doesn't violate anything
void* operator new(size_t, void* p) {return &f;}
};
但是,除非调用特定于类的放置版本,否则您的断言应该成立。两个表达式必须表示与另一个答案中解释的相同的地址(主要的一点是标准的非分配operator new
只返回指针参数而新表达式没有做任何花哨的事情),这些都不是一个指针超过某个对象的末尾,因此,通过[expr.eq] / 2,它们比较相等。
答案 1 :(得分:1)
18.6.1.3安置表格[new.delete.placement]
void * operator new(std :: size_t size,void * ptr)noexcept;
返回:ptr。
明确指出placement new
运算符返回传递给它的指针。 “返回:ptr”。无法比这更清楚。
对于我而言,就“来自放置新的返回值”而言,这几乎是最重要的:放置new
对它所放置的指针没有任何作用,并且它总是返回相同的指针。
您问题中的其他所有内容都与其他演员阵容可能发生的任何其他变更有关;但是你要特别询问展示位置new
的返回值,所以我认为你接受所有其他转换只是类型转换,并且对实际指针没有影响,而你只是询问新的位置 - 但也可以通过其他演员,并做出类似的决定。