#include <iostream>
#include <string>
using namespace std;
// 모든 반복자가 가져야 하는 인터페이스를 설계한다. - 실제 C#에는 아래의 interface가 있다.
// C# 1.0 에서는 Object 최상위클래스로 받아서 타입캐스팅을 했지만..
// C# 2.0 에서는 Generic의 도입으로 반복자 인터페이스가 편리해졌다.
template<typename T> struct IEnumerator
{
        virtual bool MoveNext() = 0;
        virtual T GetObject() = 0;
        virtual void Reset() = 0;
};
//-------------------------------------------------------
// 또한 반복자를 가질 수 있는 모든 컨테이너는 아래의 인터페이스를 제공해야 한다.
template<typename T> struct IEnumerable
{
        virtual IEnumerator<T>* GetEnumerator() = 0;
};
//-------------------------------------------------------
// 열거가능한(반복자를가진) 클래스는 반드시 IEnumerable에서 파생되어야 한다.
template<typename T> class slist : public IEnumerable<T>
{
        struct Node
        {
               T     data;
               Node* next;
               Node( T a, Node* n ) : data(a), next(n) {}
        };
        Node* head;
public:
        slist() : head(0) {}
        void push_back( T a ) { head = new Node( a, head ); }
        // slist에서 값을 얻기 위한 반복자
        class SlistEnumerator : public IEnumerator<T>
        {
               Node* current;
               Node* head;
        public:
               SlistEnumerator( Node* init = 0 ) 
               : head(init), current(init) {}
               bool MoveNext() 
               { 
                       current = current->next;
                       return current != 0;
               }
               int  GetObject() { return current->data; }
               void Reset() { current = head; }
        };
        //--------------------------------------------------
        // 반복자를 리턴하는 함수를 만든다.
        IEnumerator<T>* GetEnumerator()
        {
               return new SlistEnumerator( head );
        }
};
//------------------------------------------------------
// 주어진 반복자를 사용해서 모든 요소의 합을 구한다.
template<typename T> void Sum( IEnumerator<T>* p )
{
        T s = T();
        p->Reset();
        do
        {
               s += p->GetObject();
        } while ( p->MoveNext() );
        cout << s << endl;
}
// 주어진 컨테이너의 모든 요소의 합을 구한다.
template<typename T> void Sum2( IEnumerable<T>& c )
{
        IEnumerator<T>* p = c.GetEnumerator();
        Sum( p );
}
int main()
{
        slist<int> s;
        s.push_back( 10 );
        s.push_back( 20 );
        s.push_back( 30 );
        //IEnumerator<int>* p = s.GetEnumerator();
        //Sum( p );
        Sum2( s );
        return 0;
}