Является ли законным приведение указателя на ссылку на массив с помощью static_cast в С++?

У меня есть указатель T * pValues, который я хотел бы видеть как T (&values)[N]

В этом ответе SO qaru.site/info/380318/... предлагаемый способ сделать это

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

Я думаю об этом. В его примере pValues инициализируется следующим образом

T theValues[N];
T * pValues = theValues;

Мой вопрос заключается в том, является ли конструкция трансляции законной, если pValues исходит из любой из следующих конструкций:

1

T theValues[N + M]; // M > 0
T * pValues = theValues;

2

T * pValues = new T[N + M]; // M >= 0

Ответ 1

Короткий ответ: Вы правы. Бросок безопасен, только если pValues имеет тип T[N], и оба упомянутых вами случая (разный размер, динамически распределенный массив), скорее всего, приведут к поведению undefined.


Самое приятное в 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;

и, скорее всего, приведет к неправильному использованию этого указателя и undefined поведение.


Другими словами:

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));

становится возможным.