STL C++: массивы могут использоваться прозрачно с функциями STL?

22
задан Ashwin Nanjappa 3 April 2009 в 15:07
поделиться

10 ответов

Ну, Вы спрашиваете о массиве. Можно просто легко получить указатель на его элементы, таким образом, он в основном сводится к вопросу, могут ли указатели использоваться прозрачно с функциями STL. Указатель на самом деле является самым мощным видом итератора. Существуют различные виды

  • Входной итератор: Только вперед и одна передача, и только читала
  • Выходной итератор: Только вперед и одна передача, и только пишет

  • Передайте итератор: Только передайте, и чтение-запись
  • Двунаправленный итератор: Передайте и назад, и чтение-запись
  • Итератор произвольного доступа: Произвольные шаги вперед и назад в одном дыхании и чтении-записи

Теперь каждый итератор во второй группе поддерживает все вещи всех итераторов, упомянутых перед ним. Указатель моделирует последний вид итераторов - итератор произвольного доступа. Можно добавить/вычесть произвольное целое число, и можно читать и записать. И все кроме выходного итератора имеют a operator-> это может использоваться для доступа к члену типа элемента, которого мы выполняем итерации.

Обычно, итераторы имеют несколько определений типов как участников

  • value_type - чего итератор выполняет итерации по (интервал, bool, строка...)
  • ссылка - ссылка на value_type
  • указатель - указатель на value_type
  • difference_type - то, что вводит расстояние между двумя итераторами, имеет (возвращенный std::distance).
  • iterator_category - это - тип тега: это - typedefed к типу, который представляет вид итератора. также 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*. Указатель является очень хорошим примером итератора даже. Поскольку это так чрезвычайно просто, но в то же время так мощно, что может выполнить итерации по диапазону.

41
ответ дан 29 November 2019 в 03:31
поделиться

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

Фактически, возможная реализация std::vector<T>::iterator состоит в том, чтобы просто сделать его T*.

Конечно, для массива у вас не будет полезных методов begin() и end() для поиска допустимого диапазона итераторов, но это проблема, которую вы всегда имеете с массивами в стиле C.

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

17
ответ дан CAdaker 29 November 2019 в 03:31
поделиться

Допускается ли в стандарте такое использование массивов с функциями 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;
4
ответ дан bayda 29 November 2019 в 03:31
поделиться

Введение в boost::array (простая шаблонная оболочка для обычных массивов, которая также определяет STL-совместимые типы итераторов и begin() / end() и т. Д.) Содержит некоторые интересное обсуждение степени их совместимости с STL.

2
ответ дан timday 29 November 2019 в 03:31
поделиться

Да, и это специально. Итераторы могут быть реализованы как указатели, и поэтому вы можете использовать указатели как итераторы.

1
ответ дан Edouard A. 29 November 2019 в 03:31
поделиться

Поскольку int a [] может рассматриваться как указатель. А в C ++ указатели можно увеличивать и указывать после этого на следующий элемент. А поскольку указатели можно сравнивать, то указатели можно использовать в качестве итераторов.

Существуют требования к итераторам, указанным в стандартном разделе 24.1. И указатели встречают их. Вот некоторые из них

Все итераторы i поддерживают выражение * i

Так же, как обычный указатель на массив гарантирует, что существует значение указателя, указывающее на последний элемент массив, поэтому для любого типа итератора есть значение итератора, которое указывает за последним элементом соответствующего контейнера.

0
ответ дан Mykola Golubyev 29 November 2019 в 03:31
поделиться

Короткий ответ: алгоритмы STL обычно определяются для работы с итераторами различных видов. Итератор определяется его поведением: это должно быть dereferenceable с *, это должно быть incrementable с ++, и различные другие вещи, которые также определяют, какой итератор это (самым общим является произвольный доступ). Помните, что алгоритмы STL являются шаблонами, таким образом, вопросом является один из синтаксиса. Точно так же экземпляр класса с оператором () определенные работы синтаксически точно так же, как функция, таким образом, они могут использоваться попеременно.

Указатель делает все, должен был быть итератор произвольного доступа. Поэтому это - итератор произвольного доступа и может использоваться как таковое в алгоритмах STL. Вы могли посмотреть на векторные реализации; Вы, очень вероятно, найдете это vector<whatever>::iterator a whatever *.

Это не делает массив допустимым контейнером STL, но он действительно делает указатели допустимыми итераторами STL.

7
ответ дан 29 November 2019 в 03:31
поделиться

Вместо того, чтобы использовать массивы и затем волновать по поводу передачи их к функциям 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, можно сделать так благодаря совместимости на уровне двоичных кодов векторов с массивами.

2
ответ дан 29 November 2019 в 03:31
поделиться

Просто комментарий к ответу 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...
}
2
ответ дан 29 November 2019 в 03:31
поделиться

Модель Pointers Тривиальный Итератор и указатель из модели Random Access Iterator массивов. Таким образом да, это совершенно законно.

Если Вы интересно на ограничениях использования каждого S (T) L алгоритм, ознакомляетесь модели итератора.

1
ответ дан 29 November 2019 в 03:31
поделиться
Другие вопросы по тегам:

Похожие вопросы: