Подтвердить что ты не робот

При объявлении ссылки на массив из Ints, почему она должна быть ссылкой на const-указатель?

Примечание. Я использую компилятор g++ (который я слышу довольно хорошо и должен быть довольно близок к стандарту).


Скажем, вы объявили массив ints:

int a[3] = { 4, 5, 6 };

Теперь позвольте сказать, что вы действительно хотите объявить ссылку на этот массив (неважно почему, кроме Bjarne, говорит, что язык поддерживает его).

Случай 1 - если вы попытаетесь:

int*& ra = a;

тогда компилятор откажется и скажет:

"invalid initialization of non-const reference of type `int*&' from a temporary of type `int*'"  

Прежде всего, почему "a" является временной переменной (т.е. не имеет ли место в памяти?)...

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

Случай 2 - если вы попробуете:

int*const&rca = a;  //wish I knew where the spaces should go (but my other post asking about this sort of protocol got a negative rank while many of the answers got ranked highly -- aha! there are stupid questions!) 

Тогда все круто, оно компилируется, и вы получаете ссылку на массив.

Случай 3 - Теперь вот еще одна вещь, которая скомпилирует:

int* justSomeIntPointer = a;  //LINE 1
int*& rpa = justSomeIntPointer;  //LINE 2

Это также дает ссылку на исходный массив.

Итак, вот мой вопрос: в какой точке имя статически объявленного массива стать const-указателем? Кажется, я помню, что имя массива ints также является указателем на int, но я не помню, чтобы он всегда был const-pointer-to-int...

Кажется, что Case 1 терпит неудачу, потому что ссылка объявлена ​​(ra) не на const-указатель, что может означать, что "a" уже был const-pointer-to-int для начала.

Кажется, что Case 2 работает, потому что ссылка объявлена ​​(rca) уже является const-указателем на int.

Дело 3 также работает, что является опрятным, но почему? В какой момент предполагаемый указатель-на-int (т.е. Имя массива 'a') становится константным указателем? Это происходит, когда вы назначаете его int * (LINE 1), или это происходит, когда вы присваиваете этому int * int * & (LINE 2)?

Надеюсь, это имеет смысл. Спасибо.

4b9b3361

Ответ 1

int*& ra = a;

int* - тип указателя, а не тип массива. Поэтому, чтобы он не привязывался к a, который имеет тип int[3].

int* const& ra = a;

работает, потому что он эквивалентен

int* const& ra = (int*)a;

То есть временный указатель концептуально создается с правой стороны присваивания, и это временное ограничение затем привязывается к ra. Поэтому, в конце концов, это не лучше:

int* ra = a;

где ra на самом деле является указателем на первый элемент массива, а не ссылкой на массив.

Объявление ссылки на массив простым способом:

typedef int array_type[3];
array_type& ra = a;

Непростой способ:

int (&ra)[3] = a;

С++ 11-простой способ:

auto& ra = a;

В какой момент имя статически объявленного массива становится const-указателем? Кажется, я помню, что имя массива ints также является указателем на int, но я не помню, чтобы он всегда был const-pointer-to-int...

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

  • распад происходит при попытке любого типа "копирования" (поскольку C не позволяет копировать массивы напрямую)
  • распад является своего рода преобразованием и может произойти в любое время, когда разрешено преобразование: когда типы не соответствуют

Первый вид обычно происходит с шаблонами. Таким образом, template<typename T> pass_by_value(T);, то pass_by_value(a) фактически передаст int*, потому что массив типа int[3] не может быть скопирован.

Что касается второго, вы уже видели его в действии: это происходит во втором случае, когда int* const& не может привязываться к int[3], но может привязываться к временному int*, поэтому преобразование случается.

Ответ 2

Слово "массив" в С++ написано с помощью скобок []. Если вы хотите объявить что-то-что-то на С++, у вас должны быть скобки в вашей декларации. Если вы вместо этого напишите звездочку *, вы получите указатель. Указатели и массивы - это две разные вещи.

Это ссылка на массив:

int (&ra) [3] = a;

Ответ 3

Очень большая ошибка (также очень хороший вопрос для интервью), который большинство людей делает, это то, что они считают, что имя массива эквивалентно указателю. Это неправда. Эта ошибка вызывает множество ошибок в программах на C, особенно связанных с ошибками, и их очень сложно отладить. Дифференция такова: имя массива - это указатель на первый элемент структуры, массив. Тип имени массива - это не указатель, а тип массива. С другой стороны, указатель - это просто указатель на одну вещь без какой-либо другой информации. Тип указателя - это указатель. Массивtype имеет некоторые другие свойства, например, знает ли он в стеке или нет; следовательно, "временный". Временная ошибка в вашем случае происходит из проверки, которая препятствует назначению временной переменной для ссылки. Ключевое слово const вызывает проверку. С другой стороны, у указателя есть понятие "временное". Теперь предположим, что вы хотите обмануть компилятор и назначить ссылку на то, что находится в стеке. В этом случае вам нужно сделать это указателем. Как?

Int * & ra = & a [0];

в приведенном выше случае вы сначала получите значение и используя & (адрес оператора), вы создаете указательType. Теперь указатель не имеет информации о том, находится ли он в стеке (временная переменная) или нет. Это, однако, сделает ссылку на указатель на первый элемент массива. (Поэтому просто тип указателя, а не тип массива)

Ответ 4

Если вам действительно нужна ссылка на массив, вы должны использовать следующее:

int a[3] = { 4, 5, 6 };
int (&ra)[3] = a;

То, что вы пытаетесь создать с помощью int *&, является ссылкой на указатель на int. Это не тот же тип. И поскольку вы инициализируете ссылку со значением, которое не может измениться (адрес массива), вы должны объявить указатель const (а не int).

Ответ 5

У вас есть массив ints:

int a[3] = { 4, 5, 6 };

Теперь эта строка:

int*& ra = a;

создает ссылку на указатель. Поскольку вы создаете временный указатель (преобразованный из массива a), компилятор жалуется, потому что стандарт запрещает присвоение временным ссылкам ссылке.

Итак, чтобы исправить это, вам нужно создать указатель, а затем назначить его ссылке на указатель:

int *pa = a;
int *& rpa = pa;

Постоянные ссылки могут содержать ссылку на временные разделы, но вы уже обнаружили это.

Что вы спросили (о ссылке на массив) - самый известный пример создания ссылки на массив:

template< typename T, size_t N >
size_t ArraySize( T (&)[ N ] )
{
    return N;
}

Эта функция принимает ссылку на массив и возвращает ее размер.

Ответ 6

В какой момент имя статически объявленного массива становится const-указателем? Кажется, я помню, что имя массива ints также является указателем на int, но я не помню, чтобы он всегда был const-pointer-to-int...

Поскольку вы записывали значения прямо там, в файле cpp, поэтому константа.

Вы можете просто использовать:

const int *pToArray = a;

или

const int *pToArray = (const int*)&a[0];

Ответ 7

a - временная переменная, потому что вы объявили ее в стеке, а не в куче, используя malloc или новый.