gpt4 book ai didi

c++ - 将 char 数组转换为对象指针 - 这是 UB 吗?

转载 作者:可可西里 更新时间:2023-11-01 18:24:16 26 4
gpt4 key购买 nike

我最近看到一个类似这样的类,它用于“按需”构造对象,而不必出于各种原因使用动态内存分配。

#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)];
};

此代码(即将正确对齐的 char 数组转换为对象指针)是否被 C++14 标准视为未定义行为,还是完全没问题?

最佳答案

这个程序在技术上有未定义的行为,尽管它可能适用于大多数实现。问题是来自 char* 的类型转换至 T*不保证产生指向 T 的有效指针由 placement new 创建的对象,即使 char*指针表示用于存储 T 的第一个字节的地址对象。

[basic.compound]/3 :

Pointers to layout-compatible types shall have the same value representation and alignment requirements ([basic.align]).

一般来说,T不会与 char 布局兼容或 alignas(T) char[sizeof(T)] , 所以不需要指针 T*与指针具有相同的值表示 char*void* .

[basic.compound]/4 :

Two objects a and b are pointer-interconvertible if:

  • they are the same object, or

  • one is a union object and the other is a non-static data member of that object ([class.union]), or

  • one is a standard-layout class object and the other is the first non-static data member of that object, or, if the object has no non-static data members, any base class subobject of that object ([class.mem]), or

  • there exists an object c such that a and c are pointer-interconvertible, and c and b are pointer-interconvertible.

If two objects are pointer-interconvertible, then they have the same address, and it is possible to obtain a pointer to one from a pointer to the other via a reinterpret_cast. [ Note: An array object and its first element are not pointer-interconvertible, even though they have the same address. — end note ]

[旁白:DR 2287在 C++17 发布后,在第二个项目符号中将“标准布局 union 体”更改为“union 体”。但这并不影响这个程序。]

T放置 new 创建的对象不能与 object_ 进行指针互换或 object_[0] .注释暗示这可能是类型转换的问题......

对于 C 风格的转换 ((T*)object_) , 我们需要看 [expr.cast]/4 :

The conversions performed by

  • a const_cast,

  • a static_cast,

  • a static_cast followed by a const_cast,

  • a reinterpret_cast, or

  • a reinterpret_cast followed by a const_cast

can be performed using the cast notation of explicit type conversion....

If a conversion can be interpreted in more than one of the ways listed above, the interpretation that appears first in the list is used, even if a cast resulting from that interpretation is ill-formed.

除非Tchar或简历合格 char , 这实际上是一个 reinterpret_cast ,所以接下来我们看[expr.reinterpret.cast]/7 :

An object pointer can be explicitly converted to an object pointer of a different type. When a prvalue v of object pointer type is converted to the object pointer type "pointer to cv T", the result is static_­cast<cv T*>(static_­cast<cv void*>(v)).

所以首先我们有一个 static_cast来自 char*void* ,它执行 [conv.ptr]/2 中描述的标准转换:

A prvalue of type "pointer to cv T", where T is an object type, can be converted to a prvalue of type "pointer to cv void". The pointer value ([basic.compound]) is unchanged by this conversion.

后面跟着一个 static_cast来自 void*T* ,描述于 [expr.static.cast]/13 :

A prvalue of type "pointer to cv1 void" can be converted to a prvalue of type "pointer to cv2 T", where T is an object type and cv2 is the same cv-qualification as, or greater cv-qualification than, cv1. If the original pointer value represents the address A of a byte in memory and A does not satisfy the alignment requirement of T, then the resulting pointer value is unspecified. Otherwise, if the original pointer value points to an object a, and there is an object b of type T (ignoring cv-qualification) that is pointer-interconvertible with a, the result is a pointer to b. Otherwise, the pointer value is unchanged by the conversion.

如前所述,T 类型的对象不能与 object_[0] 进行指针互换,所以这句话不适用,也不能保证结果 T*指向 T目的!我们剩下的句子是“指针值未更改”,但如果 char* 的值表示,这可能不是我们想要的结果。和 T*指针差异太大。

可以使用 union 实现此类的符合标准的版本:

template<typename T>
class StaticObject
{
public:
StaticObject() : constructed_(false), dummy_(0) {}
~StaticObject()
{
if (constructed_)
object_.~T();
}
StaticObject(const StaticObject&) = delete; // or implement
StaticObject& operator=(const StaticObject&) = delete; // or implement

void construct()
{
assert(!constructed_);

new(&object_) T;
constructed_ = true;
}

T& operator*()
{
assert(constructed_);

return object_;
}

const T& operator*() const
{
assert(constructed_);

return object_;
}

private:
bool constructed_;
union {
unsigned char dummy_;
T object_;
}
};

或者更好,因为这个类本质上是在尝试实现 optional , 只需使用 std::optional 如果你有它或 boost::optional 如果你不这样做。

关于c++ - 将 char 数组转换为对象指针 - 这是 UB 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51231757/

26 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com