Использование итераторов на массивах
В С++ 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
в указатель, и трюк больше не будет работать.