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

Существует ли стандартный С++ эквивалент IEnumerable <T> в С#?

Или безопасно использовать вектор, если Enumerator of T просто перечисляет все элементы?

4b9b3361

Ответ 1

В С++ он не нужен, и вот почему:

С# поддерживает только динамический полиморфизм. Поэтому для создания алгоритма многократного использования вам нужен интерфейс, который будет реализован всеми итераторами. Это IEnumerator<T> и IEnumerable<T> является factory для возврата итератора.

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

Контейнеры С++ и итераторы имеют неявные интерфейсы, которые эквивалентны .NET IEnumerable<T>, IEnumerator<T>, ICollection<T>, IList<T>, а именно:

Для контейнеров:

  • iterator и const_iterator typedefs
  • begin() Функция-член - заполняет необходимость IEnumerable<T>::GetEnumerator()
  • end() функция-член - вместо IEnumerator<T>::MoveNext() возвращаемое значение

Для форвардных итераторов:

  • value_type typedef
  • operator++ - вместо IEnumerator<T>::MoveNext()
  • operator* и operator-> - вместо IEnumerator<T>::Current
  • ссылочный тип возврата из operator* - вместо IList<T> установщик индексатора
  • operator== и operator!= - нет истинного эквивалента в .NET, но с контейнером end() соответствует IEnumerator<T>::MoveNext() возвращаемое значение

Для итераторов произвольного доступа:

  • operator+, operator-, operator[] - вместо IList<T>

Если вы их определяете, стандартные алгоритмы будут работать с вашим контейнером и итератором. Интерфейс не требуется, никаких виртуальных функций не требуется. Не использование виртуальных функций делает С++ общий код быстрее, чем эквивалентный код .NET, иногда намного быстрее.


Примечание: при написании общих алгоритмов лучше использовать std::begin(container) и std::end(container) вместо функций-членов контейнера. Это позволяет использовать ваш алгоритм с необработанными массивами (которые не имеют функций-членов) в дополнение к контейнерам STL. Исходные массивы и необработанные указатели удовлетворяют всем остальным требованиям контейнеров и итераторов с этим единственным исключением.

Ответ 2

Стандартный способ С++ состоит в том, чтобы передать два итератора:

template<typename ForwardIterator>
void some_function(ForwardIterator begin, ForwardIterator end)
{
    for (; begin != end; ++begin)
    {
        do_something_with(*begin);
    }
}

Пример кода клиента:

std::vector<int> vec = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(vec.begin(), vec.end());

std::list<int> lst = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(lst.begin(), lst.end());

int arr[] = {2, 3, 5, 7, 11, 13, 17, 19};
some_function(arr + 0, arr + 8);

Я обобщенное программирование!

Ответ 3

IEnumerable<T> концептуально сильно отличается от vector.

IEnumerable обеспечивает прямой доступ только для чтения к последовательности объектов, независимо от того, какой контейнер (если есть) содержит объекты. A vector - фактически сам контейнер.

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

Хорошим примером является определение С++ STL accumulate, что можно сравнить с IEnumerable < Т > .Aggregate

В С++

   int GetProduct(const vector<int>& v)
   {
         // We don't provide the container, but two iterators
         return std::accumulate(v.begin(), v.end(), 1, multiplies<int>());
   }

В С#

  int GetProduct(IEnumerable<int> v)
  {
        v.Aggregate(1, (l, r) => l*r);
  }

Ответ 4

Если мы строго придерживаемся вопроса, ответ не так, насколько я знаю. Люди все время отвечали, что есть замена, доступная на С++, которая может быть хорошей информацией, но не ответами, и что Дереке, скорее всего, уже знал.

Я полностью не согласен с тем, что "он не нужен", просто разные версии стандартных библиотек С++ и .NET. Главная особенность IEnumerable < > заключается в том, что она является полиморфной, и поэтому она позволяет вызывающему пользователю использовать любой класс, который он хочет (массив, список, набор и т.д.), Сохраняя при этом сильную типизацию при компиляции, отказоустойчивую даже в библиотеке apis.

Единственная альтернатива в С++ - это шаблоны. Но шаблоны С++ не содержат безопасных типизированных дженериков во время выполнения, они в основном являются макросами. Итак, в первую очередь с шаблонами на С++ вы вынуждены предоставить весь исходный код шаблона тем, кто должен использовать ваш шаблон. Более того, если вы создадите свою библиотеку api templated, вы потеряете возможность гарантировать, что вызов на нее будет скомпилирован, а код не будет автоматически самозарегистрироваться.

Я полностью сочувствую любому другому программисту, который использует С# и С++ и разочарован этим моментом. На мой взгляд, итераторы std уступают дизайну по сравнению с более современными идеями. Производительность вряд ли является аргументом, если я этого хочу, я всегда мог использовать необработанные указатели; особенно потому, что итераторы - это немного больше, чем тонко обернутые указатели. Попробуйте удалить ссылку на итератор, который происходит во время выполнения до ==.end(), потому что вы забыли проверить, и вы получите недопустимую ссылку - даже если вся идея дизайна С++, избыточно вводившая ссылки, помимо указателей C, была что первые якобы гарантированно были недействительными или недействительными.