Когда auto используется против массива, почему он преобразован в указатель, а не в ссылку?
См. пример ниже:
int arr[10];
int *p = arr; // 1st valid choice
int (&r)[10] = arr; // 2nd valid choice
Теперь, когда мы используем auto
против arr
, тогда он выбирает первый вариант.
auto x = arr; // x is equivalent to *p
Есть ли причина для выбора указателя, а не ссылки для массива?
Ответы
Ответ 1
Да. В этом выражении массив распадается на тип указателя, из-за преобразования lvalue-to-rvalue
.
Если вам нужен тип массива, а не тип указателя, сделайте следующее:
auto & x = arr; //now it doesn't decay into pointer type!
&
в целевом типе предотвращает разложение массива в тип указателя!
x
- это массив, а не указатель, можно доказать как:
void f(int (&a)[10])
{
std::cout << "f() is called. that means, x is an array!" << std::endl;
}
int main() {
int arr[10];
auto & x = arr;
f(x); //okay iff x is an int array of size 10, else compilation error!
}
Вывод:
f() is called. that means, x is an array!
Демо на идеоне: http://www.ideone.com/L2Ifp
Обратите внимание, что f
нельзя вызвать с типом указателя. Его можно вызвать с помощью массива int
размера 10
. Попытка вызвать его любым другим типом приведет к ошибке компиляции.
Ответ 2
Чтобы предоставить стандартную ссылку для поведения, пункт 7.1.6.4 [dcl.spec.auto] в пункте 6 гласит:
Как только тип идентификатора-декларатора был определен в соответствии с 8.3, тип объявленной переменной с использованием идентификатора-декларатора определяется из типа его инициализатора, используя правила для вычитания аргумента шаблона. Пусть T - тип, который был определен для идентификатора переменной d. Получите P из T, заменив вхождения auto на... новый шаблонный шаблонный шаблон U... Тип, выведенный для переменной d, затем выведенный A определяется с использованием правил вывода аргумента шаблона из вызова функции (14.8.2.1), где P - тип параметра шаблона функции, а инициализатор для d - соответствующий аргумент. Если вычет заканчивается, декларация плохо сформирована.
Поэтому нам нужно искать в другом месте, в частности, 14.8.2.1 [tmp.deduct.call], пункт 2:
Если P не является ссылочным типом: - Если A является типом массива, то тип указателя, созданный стандартным преобразованием от массива к указателю (4.2), используется вместо A для вывода типа
Для полноты ради, 4.2 [conv.array], пункт 1:
Значение lvalue или rvalue типа "массив из N T" или "массив неизвестной границы T" может быть преобразовано в prvalue типа "указатель на T". Результатом является указатель на первый элемент массива.
Чтобы выполнить его, auto x = arr;
создает мнимую функцию template<typename P> f(P);
и пытается вывести P
из вызова f(arr)
. A
в этом случае array of 10 int
, а P
не является ссылочным типом, поэтому A
вместо этого становится pointer to int
. Которая перколит цепочку обратно в конечный тип x
.
Итак, в основном, он рассматривается как указатель, потому что правила говорят, что это необходимо. Поведение просто более полезно таким образом, поскольку массивы не назначаются. В противном случае ваш auto x = arr;
не смог бы скомпилировать, а не сделать что-то полезное.
Ответ 3
auto
создает значения. Он не будет давать ссылок. Имея это в виду, тогда указатель представляет собой тривиальное преобразование в значение, которое предлагает массив.
Ответ 4
Для массива arr
само выражение arr
означает &arr[0]
. Это правило исходит из C. Поэтому IMO автоматически выбирает указатель.