При объявлении ссылки на массив целых чисел, почему она должна быть ссылкой на const-указатель? - PullRequest
14 голосов
/ 26 июля 2011

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


Допустим, вы объявили массив целых:

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

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

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

int*& ra = a;

затем компилятор останавливается и говорит:

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

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

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

Случай 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-указателем?Кажется, я помню, что имя массива int также указатель на int, но я не помню, чтобы он когда-либо был const-pointer-to-int ...

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

Это похоже на случай 2работает, потому что объявленная ссылка (rca) уже является const-pointer-to-int.

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

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

Ответы [ 7 ]

30 голосов
/ 26 июля 2011
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;

C ++ 11-легкий способ:

auto& ra = a;

В какой момент имя статически объявленного массива становится const-указателем? Кажется, я помню, что имя массива int также является указателем на 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*, поэтому преобразование случается.

8 голосов
/ 26 июля 2011

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

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

int (&ra) [3] = a;
2 голосов
/ 26 июля 2011

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

int * & ra = & a [0];

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

1 голос
/ 26 июля 2011

У вас есть массив целых:

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

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

1 голос
/ 26 июля 2011

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

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

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

0 голосов
/ 26 июля 2011

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

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

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

const int *pToArray = a;

или

const int *pToArray = (const int*)&a[0];
0 голосов
/ 26 июля 2011

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

...