Ну, Вы спрашиваете о массиве. Можно просто легко получить указатель на его элементы, таким образом, он в основном сводится к вопросу, могут ли указатели использоваться прозрачно с функциями STL. Указатель на самом деле является самым мощным видом итератора. Существуют различные виды
Теперь каждый итератор во второй группе поддерживает все вещи всех итераторов, упомянутых перед ним. Указатель моделирует последний вид итераторов - итератор произвольного доступа. Можно добавить/вычесть произвольное целое число, и можно читать и записать. И все кроме выходного итератора имеют a operator->
это может использоваться для доступа к члену типа элемента, которого мы выполняем итерации.
Обычно, итераторы имеют несколько определений типов как участников
std::distance
). std::input_iterator_tag
, ..., std::random_access_iterator_tag
. Алгоритмы могут использовать его для перегрузки на различных видах итераторов (как std::distance
быстрее для итераторов произвольного доступа, потому что это может просто возвратиться a - b
)Теперь, указатель, конечно, не имеет тех участников. C++ имеет iterator_traits
обработайте по шаблону и специализирует его для указателей. Таким образом, если Вы хотите получить тип значения какого-либо итератора, Вы делаете
iterator_traits<T>::value_type
И ли это будет указатель или некоторый другой итератор, то это даст Вам value_type того итератора.
Таким образом - да, указатель может очень хорошо использоваться с алгоритмами STL. Поскольку кто-то еще упомянул, даже std::vector<T>::iterator
может быть a T*
. Указатель является очень хорошим примером итератора даже. Поскольку это так чрезвычайно просто, но в то же время так мощно, что может выполнить итерации по диапазону.
Стандарт разработал итераторы так, чтобы они чувствовали и вели себя как указатели, насколько это возможно. Кроме того, поскольку итераторы основаны на шаблонах, единственная важная вещь заключается в том, что для типа итератора определены правильные операторы. Результатом является то, что указатели из коробки будут вести себя так же, как итераторы с произвольным доступом.
Фактически, возможная реализация std::vector<T>::iterator
состоит в том, чтобы просто сделать его T*
.
Конечно, для массива у вас не будет полезных методов begin()
и end()
для поиска допустимого диапазона итераторов, но это проблема, которую вы всегда имеете с массивами в стиле C.
Редактировать: На самом деле, как уже упоминалось в комментариях и других ответах, вы можете реализовать эти функции для массивов, если массив не является динамическим и не распался на указатель. Но моя основная мысль заключалась в том, что вы должны быть более осторожными, чем при использовании стандартных контейнеров.
Допускается ли в стандарте такое использование массивов с функциями STL?
да
Если да, то как архаичные структуры, такие как массивы, вписываются в грандиозный план STL шаблонных итераторов, контейнеров и функций?
Итераторы были разработаны с аналогичной семантикой как указатели.
Кроме того, есть ли какие-либо предостережения или детали в таком использовании, о которых программист должен быть осторожен?
Я предпочитаю следующее использование:
int a[] = {9, 8, 7};
const size_t a_size = lengthof( a );
cerr << "Sum: " << accumulate( a, a + a_size , 0, plus<int>()) << endl;
Или гораздо лучше и безопаснее использовать boost :: array:
boost::array< int, 3 > a = { 9, 8, 7 };
cerr << "Sum: " << accumulate( a.begin(), a.end(), 0, plus<int>()) << endl;
Введение в boost::array
(простая шаблонная оболочка для обычных массивов, которая также определяет STL-совместимые типы итераторов и begin()
/ end()
и т. Д.) Содержит некоторые интересное обсуждение степени их совместимости с STL.
Да, и это специально. Итераторы могут быть реализованы как указатели, и поэтому вы можете использовать указатели как итераторы.
Поскольку int a [] может рассматриваться как указатель. А в C ++ указатели можно увеличивать и указывать после этого на следующий элемент. А поскольку указатели можно сравнивать, то указатели можно использовать в качестве итераторов.
Существуют требования к итераторам, указанным в стандартном разделе 24.1. И указатели встречают их. Вот некоторые из них
Все итераторы i поддерживают выражение * i
Так же, как обычный указатель на массив гарантирует, что существует значение указателя, указывающее на последний элемент массив, поэтому для любого типа итератора есть значение итератора, которое указывает за последним элементом соответствующего контейнера.
Короткий ответ: алгоритмы STL обычно определяются для работы с итераторами различных видов. Итератор определяется его поведением: это должно быть dereferenceable с *, это должно быть incrementable с ++, и различные другие вещи, которые также определяют, какой итератор это (самым общим является произвольный доступ). Помните, что алгоритмы STL являются шаблонами, таким образом, вопросом является один из синтаксиса. Точно так же экземпляр класса с оператором () определенные работы синтаксически точно так же, как функция, таким образом, они могут использоваться попеременно.
Указатель делает все, должен был быть итератор произвольного доступа. Поэтому это - итератор произвольного доступа и может использоваться как таковое в алгоритмах STL. Вы могли посмотреть на векторные реализации; Вы, очень вероятно, найдете это vector<whatever>::iterator
a whatever *
.
Это не делает массив допустимым контейнером STL, но он действительно делает указатели допустимыми итераторами STL.
Вместо того, чтобы использовать массивы и затем волновать по поводу передачи их к функциям STL (что можно было бы назвать 'прямой совместимостью' и поэтому хрупок), IMO, необходимо использовать станд.:: вектор и использование (стабильный и надежный) назад совместимость с функциями, которые берут массивы, должны, когда-либо необходимо использовать их.
Таким образом, Ваш код становится:
#include <functional>
#include <iostream>
#include <numeric>
#include <vector>
using namespace std;
int main()
{
vector<int> a(3);
a[0] = 9;
a[1] = 8;
a[2] = 7;
cerr << "Sum: " << accumulate(a.begin(), a.end(), 0, plus<int>()) << endl;
return 0;
}
И если когда-нибудь необходимо передавать API C, можно сделать так благодаря совместимости на уровне двоичных кодов векторов с массивами.
Просто комментарий к ответу Mykola:
Массивы не являются указателями, даже если они имеют тенденцию затухать в указатели действительно легко. Компилятор имеет больше информации о массиве, чем на контейнере:
namespace array {
template <typename T, int N>
size_t size( T (&a)[N] ) {
return N;
}
template <typename T, int N>
T* begin( T (&a)[N] ) {
return &a[0];
}
template <typename T, int N>
T* end( T (&a)[N] ) {
return &a[N];
}
}
int main()
{
int theArray[] = { 1, 2, 3, 4 };
std::cout << array::size( theArray ) << std::endl; // will print 4
std::cout
<< std::accumulate( array::begin( theArray ), array::end( theArray ), 0, std::plus<int>() )
<< std::endl; // will print 10
}
В то время как Вы не можете спросить о размере массива, компилятор разрешит его при вызове данных шаблонов.
Теперь, если Вы вызываете функцию, которая берет a int a[]
(обратите внимание, что нет никакого размера), который подобен определению int*
параметр и информация о размере потеряны в пути. Компилятор не сможет определить размер массива в функции: массив затух в указатель.
Если с другой стороны, Вы определяете параметр как int a[10]
затем информация потеряна, но Вы не сможете вызвать функцию с массивом другого размера. Это полностью отличается, чем версия C, по крайней мере, до C99 не проверили в последнее время [*]. В C компилятор проигнорирует число в аргументе, и подпись будет эквивалентна предыдущей версии.
@litb: Вы правы. У меня был этот тест вокруг, но это со ссылкой на массив, не с массивом. Спасибо за указание на него.
dribeas@golden:array_size$ cat test.cpp
void f( int (&x)[10] ) {}
int main()
{
int array[20];
f( array ); // invalid initialization of reference of type 'int (&)[10]' from...
}
Модель Pointers Тривиальный Итератор и указатель из модели Random Access Iterator массивов. Таким образом да, это совершенно законно.
Если Вы интересно на ограничениях использования каждого S (T) L алгоритм, ознакомляетесь модели итератора.