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

Когда 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

Есть ли причина для выбора указателя, а не ссылки для массива?

4b9b3361

Ответ 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 автоматически выбирает указатель.