在 C++ 中使用 static_cast 将指针强制转换为数组引用是否合法?

问题描述 投票:0回答:2

SO是一场狗屎秀。感谢您的搭车。

c++ arrays casting type-safety static-cast
2个回答
10
投票

简短回答:你是对的。仅当

pValues
的类型为
T[N]
时,转换才是安全的,并且您提到的两种情况(不同大小、动态分配的数组)很可能会导致 未定义的行为


static_cast
的好处是在编译时进行了一些额外的检查,所以如果你似乎做错了什么,编译器会抱怨它(与丑陋的C风格转换相比,它允许你做几乎任何事情) ,例如:

struct A { int i; };
struct C { double d; };

int main() {
    A a;
    // C* c = (C*) &a; // possible to compile, but leads to undefined behavior
    C* c = static_cast<C*>(&a);
}

会给你:

invalid static_cast from type ‘A*’ to type ‘C*’

在这种情况下,您转换为

void*
,从可以在编译时进行的检查的角度来看,这对于几乎任何内容都是合法的,反之亦然:
void*
也可以转换回几乎任何内容,这使得
static_cast
的使用一开始就完全没用,因为这些检查变得毫无用处。

对于前面的例子:

C* c = static_cast<C*>(static_cast<void*>(&a));

并不比:

更好
C* c = (C*) &a;

并且很可能会导致该指针的错误使用和未定义的行为


换句话说:

A arr[N];
A (&ref)[N] = *static_cast<A(*)[N]>(&arr);

很安全,很好。但一旦你开始滥用

static_cast<void*>
,就根本无法保证实际会发生什么,因为即使是这样的东西:

C *pC = new C;
A (&ref2)[N] = *static_cast<A(*)[N]>(static_cast<void*>(&pC));

成为可能。


1
投票

自 C++17 起,至少显示的表达式不安全,即使

pValues
是指向数组第一个元素的指针并且数组具有完全匹配的类型(包括 excat 大小),无论是否从变量获取声明或致电
new
。 (如果不满足这些标准,无论以下情况如何,都是 UB。)

数组及其第一个元素不是 指针可互转换,因此

reinterpret_cast
(相当于两个
static_casts
void*
)无法将一个的指针值转换为另一个的指针值。

因此

static_cast<T(*)[N]>(static_cast<void*>(pValues))
仍将指向数组的第一个元素,而不是数组对象本身。

由于类型/值不匹配,因此引用此指针是未定义的行为。

这可以通过

std::launder
来解决,这可能会改变
reinterpret_cast
无法改变的指针值。具体来说,以下内容可能是明确定义的:

T (&values)[N] = *std::launder(static_cast<T(*)[N]>(static_cast<void*>(pValues)));

或同等程度

T (&values)[N] = *std::launder(reinterpret_cast<T(*)[N]>(pValues));

但前提是

std::launder
返回的指针不能用于访问无法通过原始
pValues
指针访问的任何字节。如果数组是一个完整的对象,则满足这一点,但是例如如果数组是二维数组的子数组则不满足。

有关确切的可达性条件,请参阅 https://en.cppreference.com/w/cpp/utility/launder

© www.soinside.com 2019 - 2024. All rights reserved.