Использование итераторов на массивах

В С++ Primer указано, что

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

Я хотел использовать итераторы для печати массива. Программа ниже работает нормально, но когда я пытаюсь напечатать arr2 или arr3, если я не ошибаюсь, что имеет тип int *, я получаю сообщение об ошибке (судя, что оператор и оператор ссылается ниже).

error: no matching function for call to ‘begin(int*&)’

int main(int argc, char** argv) {

    int arr[] = {0,1,2,3,4,5,6,7,8,9};
    auto arr2 = arr;
    auto arr3(arr);   // I think arr2 and arr3 are of same type

    for(auto it = std::begin(arr) ; it != std::end(arr) ; ++it)
        std::cout << *it << " ";
    std::cout << std::endl;

    return 0;
}

Учитывая утверждение, если массив преобразован в указатель компилятором, как эта программа работает для печати содержимого arr с помощью std::begin() и std::end() и не работает для arr2 или arr3 если все они являются указателями на целые числа?


Edit

Простите, если я не могу это прояснить. Надеюсь, что я разъясню проблему с помощью этого редактирования.

Теперь, когда я знаю, что begin() и end() не будут работать с указателями (спасибо ответам), мне интересно, не цитируется ли цитируемый текст, поскольку он указывает, что существует Array → Указатель. Если какой текст говорит true, то тип arr должен быть указателем. Есть ли проблема с цитируемым текстом в этот момент?

Кроме того, есть ли способ, которым я могу использовать begin() и end() для указателей (а не контейнеров STL) с указанием размера, возможно, используя следующий конструктор?

template< class T, size_t N > 
T* begin( T (&array)[N] );

Ответы

Ответ 1

Массив легко преобразуется в указатель, но не всегда. Например, если вы берете адрес массива или получаете ссылку, исходный тип массива не теряется:

int a[10];
int (&ar)[10] = a; // fine
int (*ap)[10] = &a; // also fine

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

В вашем примере вы можете использовать arr2, если вы сделаете его ссылкой:

 auto &arr2 = arr;

Теперь arr2 имеет тип int (&)[10] вместо int *.

Ответ 2

Потому что std::begin определяется для массивов, но не для указателей. Тип массива не совпадает с типом указателя.

Проблема заключается в том, что, по-видимому, auto arr2 = arr деградирует тип от типа массива к типу указателя.

Обратите внимание, что проблема на самом деле находится в std::end не в std::begin. В конце концов, как std::end сможет указать указатель на последний элемент + 1, когда все, что у него есть, является указателем на первый элемент? Он не может, поэтому std::end не может быть определен для типа указателя и, следовательно, std::begin не имеет никакого смысла для типа указателя.

Чтобы быть точным, тип arr равен int[10], а тип arr2 и arr3 - int*. Первые могут деградировать в последнее, но не наоборот.

Ответ 3

Здесь auto распадет arr (тип массива) на указатель. И std::begin и std::end может работать только для контейнеров или массивов, но не для указателей.

Из стандарта С++ 11

§7.1.6.4 авто-спецификатор [dcl.spec.auto]

1 Автоматический тип-спецификатор означает, что тип объявляемой переменной должен быть выведен из его инициализатораили что декларатор функции должен включать тип возвращаемого типа возврата.

Вы не можете работать здесь, так как auto не может выводить типы массивов. Также как:

char a[5];
auto b[5] = a;  // error, a evaluates to a pointer, which does
                // not match the array type

Чтобы заставить его работать, используйте только контейнеры С++, например std::vector:

vector<int> arr = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 9 };

Ответ 4

std::begin и std::end работают с параметрами, которые являются массивами стилей C.

Возможная реализация std::end:

template<class T, std::size_t sizeOfArray>
constexpr T *end(T (&array)[sizeOfArray])
{
  return &array[sizeOfArray];
}

Этот способ arr не преобразуется в указатель, когда вы вызываете std::end(arr) (и информация о размере массива не теряется... функция принимает только массивы с точными sizeOfArray элементами в качестве аргумента).

template<class T>
T *end(T array[])
{
  // ?
}

не будет работать, поскольку T array[] ведет себя как плоский указатель (T *array), а не реальный массив, когда он используется как параметр функции.

auto распаковывает массив arr в указатель, и трюк больше не будет работать.