Является ли reinterpret_cast массивом в стиле C незаконным С++ 11?

Почему следующий код не одобряется?

double d[4] = {0,1,2,3};
reinterpret_cast<double[2]>(d);

GCC объявляет его invalid cast from type 'double*' to type 'double [2]', а clang объявляет, что reinterpret_cast from 'double *' to 'double [2]' is not allowed

Теперь, если намерение не очевидно, я хотел бы, чтобы этот код возвращал double[2], который содержит {0,1}, почти как reinterpret_cast<double*>(d). (Поэтому я знаю, что это будет работать с указателями, так что я не об этом спрашиваю)


person Gurg Hackpof    schedule 29.08.2013    source источник
comment
Никакое выражение не может дать массив в C или C++ — массивы не могут быть значениями r. У них всегда есть имя и адрес.   -  person JohannesD    schedule 29.08.2013
comment
@JohannesD - строковый литерал выражения? :П   -  person R. Martinho Fernandes    schedule 29.08.2013
comment
@ R.MartinhoFernandes Черт, ты меня понял: P Кроме того, в C99 есть литералы массивов ... Ну ладно. Возможно, правильно будет сказать, что во время выполнения не может быть временных объектов-массивов. Строковые литералы (и, предположительно, литералы массива C99) по-прежнему являются lvalue, а не rvalue.   -  person JohannesD    schedule 29.08.2013
comment
@JohannesD using type = int[]; type{1, 2, 3, 4, 5}; ‹-- временный массив.   -  person Simple    schedule 29.08.2013
comment
@JohannesD ха-ха! Попался и там (человек, С++ перепутался)   -  person R. Martinho Fernandes    schedule 29.08.2013
comment
Анонимный downvoter: пожалуйста, просветите меня, почему вы считаете мой искренний вопрос плохим.   -  person Gurg Hackpof    schedule 29.08.2013
comment
@JohannesD: Так что же тогда ответит Jarod42? я озадачен   -  person Gurg Hackpof    schedule 29.08.2013
comment
@GurgHackpof ссылка на предполагаемый массив из двух элементов. Вы должны заметить, что такого массива не существует, временного или нет. (Если непонятно, я имею в виду, что не делайте этого)   -  person R. Martinho Fernandes    schedule 30.08.2013


Ответы (3)


Оба компилятора правы.

reinterpret_cast это не молоток, это мощный точный инструмент. Все виды использования reinterpret_cast должны включать по крайней мере один указатель или ссылочный тип в качестве источника или назначения, за исключением вырожденного случая тождественного интегрального преобразования (т. е. reinterpret_cast из int в int разрешено и ничего не делает).

person R. Martinho Fernandes    schedule 29.08.2013

Что вы можете хотеть, это

double (&d2)[2] = reinterpret_cast<double(&)[2]>(d);

Не уверен, что это не педантично неопределенное поведение (как и большинство случаев использования reinterpret_cast).

person Jarod42    schedule 29.08.2013
comment
Хорошо, пожалуйста, просветите меня относительно того, что эта вещь на самом деле означает. Похоже, он работает с GCC (пример), но clang предупреждает что это undefined-reinterpret-cast error: reinterpret_cast from 'double [4]' to 'double (&)[2]' has undefined behavior [-Werror,-Wundefined-reinterpret-cast] - person Gurg Hackpof; 29.08.2013
comment
@GurgHackpof, если компилятор говорит, что это ужасная идея, вероятно, так оно и есть :) - person aaronman; 29.08.2013
comment
double (&d2)[2] = *reinterpret_cast<double(*)[2]>(d); не является UB, насколько я знаю. Этот трюк можно использовать как маленькое окно в более крупный массив, например. при использовании диапазона для: char str[5]{...}; for(char c : *reinterpret_cast<char(*)[4]>(str)) {.... - person bit2shift; 01.02.2017

У вас есть массив (не указатель) размера 4. Вы не можете привести его к массиву размера 2 из-за того простого факта, что размер неверен. Аналогичным примером является то, что вы не можете привести экземпляр класса к экземпляру класса к экземпляру другого класса, потому что это просто не имеет никакого смысла. Чтобы получить массив с {0,1}, вам нужно создать совершенно новый массив.

Вот как это сделать правильно:

     double d[4] = {0,1,2,3};                                                    
     double copy[2]{};                                                           
     std::copy_n(std::begin(d),2,std::begin(copy)); 

Если вы не хотите создавать копию массива, идиоматическая вещь, которую нужно сделать, - это работать с диапазоном вместо массива, есть причина, по которой каждый алгоритм в стандартной библиотеке работает с итераторами, а не с контейнерами.

person aaronman    schedule 29.08.2013
comment
Ну, я пытаюсь создать псевдоним подмножества моего массива. Весь смысл в том, чтобы не копировать. - person Gurg Hackpof; 29.08.2013
comment
@GurgHackpof это единственная причина, по которой C ++ использует итераторы для всех своих алгоритмов, работает с диапазоном, не пытайтесь делать что-то сложное - person aaronman; 29.08.2013