Or is it safe to use vector if the Enumerator of T is just listing all the elements?
The standard C++ way is to pass two iterators:
template<typename ForwardIterator>
void some_function(ForwardIterator begin, ForwardIterator end)
{
for (; begin != end; ++begin)
{
do_something_with(*begin);
}
}
Example client code:
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);
Yay generic programming!
IEnumerable<T>
is conceptually very different from vector
.
The IEnumerable
provides forward-only, read-only access to a sequence of objects, regardless of what container (if any) holds the objects. A vector
is actually a container itself.
In C++, should you want to provide access to a container without giving the details of this container, the convention is to pass in two iterators representing the beginning and end of the container.
A good example is the C++ STL definition of accumulate, which can be contrasted with IEnumerable<T>.Aggregate
In C++
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>());
}
In C#
int GetProduct(IEnumerable<int> v)
{
v.Aggregate(1, (l, r) => l*r);
}
If we stick to the question strictly the answer is no, as far as I know. People have kept replying what is the substitute available in C++, which may be good info but not answers, and which the OP most probably knew already.
I completely disagree that "it is not needed," it is just that the designs of the C++ and .NET standard libraries are different. The main feature of IEnumerable<> is that it's polymorphic, and so it enables the caller to use whatever class he wants (array, List, Set etc.), while still providing compile-time strong typing, fail-safe even in library APIs.
The only alternative in C++ is templates. But C++ templates are not safely typed run-time generics, they are basically kind of macros. So first of all with templates in C++ you are forced to provide the whole template source code to whoever needs to use your template. Moreover if you make your library API templated you lose the ability to guarantee that a call to it will compile, and the code is not automatically self-documenting.
I fully sympathize with any other programmer who uses both C# and C++ and is frustrated with this point.
However C++2X is planned to add features including ranges (which may satisfy the OP?); as well as concepts (which address the weak/bad type-checking of templates -- flaw admitted by Bjarne Stroustrup himself), and modules (which may or may not help reducing the pain from header-only templates).
It isn't needed in C++, and here's why:
C# only supports dynamic polymorphism. So to create a reusable algorithm, you need an interface which all iterators will implement. That's IEnumerator<T>
, and IEnumerable<T>
is a factory for returning an iterator.
C++ templates, on the other hand, support duck typing. That means you don't need to constrain a generic type parameter by an interface in order to access members -- the compiler will look up members by name for each individual instantiation of the template.
C++ containers and iterators have implicit interfaces which is equivalent to .NET IEnumerable<T>
, IEnumerator<T>
, ICollection<T>
, IList<T>
, namely:
For containers:
iterator
and const_iterator
typedefsbegin()
member function -- fills the need for IEnumerable<T>::GetEnumerator()
end()
member function -- instead of IEnumerator<T>::MoveNext()
return valueFor forward iterators:
value_type
typedefoperator++
-- instead of IEnumerator<T>::MoveNext()
operator*
and operator->
-- instead of IEnumerator<T>::Current
operator*
-- instead of IList<T>
indexer setteroperator==
and operator!=
-- no true equivalent in .NET, but with container's end()
matches IEnumerator<T>::MoveNext()
return valueFor random access iterators:
operator+
, operator-
, operator[]
-- instead of IList<T>
If you define these, then standard algorithms will work with your container and iterator. No interface is needed, no virtual functions are needed. Not using virtual functions makes C++ generic code faster than equivalent .NET code, sometimes much faster.
Note: when writing generic algorithms, it's best to use std::begin(container)
and std::end(container)
instead of the container member functions. That allows your algorithm to be used with raw arrays (which don't have member functions) in addition to the STL containers. Raw arrays and raw pointers satisfy all other requirements of containers and iterators, with this single exception.