| #include <vector> |
| #include <algorithm> |
| #include <string> |
| #if defined (STLPORT) |
| # include <unordered_map> |
| # include <unordered_set> |
| #endif |
| |
| //#include <iostream> |
| |
| #include "cppunit/cppunit_proxy.h" |
| |
| #if !defined (STLPORT) || defined(_STLP_USE_NAMESPACES) |
| using namespace std; |
| # if defined (STLPORT) |
| using namespace std::tr1; |
| # endif |
| #endif |
| |
| // |
| // TestCase class |
| // |
| class UnorderedTest : public CPPUNIT_NS::TestCase |
| { |
| CPPUNIT_TEST_SUITE(UnorderedTest); |
| #if !defined (STLPORT) |
| CPPUNIT_IGNORE; |
| #endif |
| CPPUNIT_TEST(uset); |
| CPPUNIT_TEST(umultiset); |
| CPPUNIT_TEST(umap); |
| CPPUNIT_TEST(umultimap); |
| CPPUNIT_TEST(user_case); |
| CPPUNIT_TEST(hash_policy); |
| CPPUNIT_TEST(buckets); |
| CPPUNIT_TEST(equal_range); |
| CPPUNIT_EXPLICIT_TEST(benchmark1); |
| CPPUNIT_EXPLICIT_TEST(benchmark2); |
| #if !defined (_STLP_USE_CONTAINERS_EXTENSION) |
| CPPUNIT_IGNORE; |
| #endif |
| CPPUNIT_TEST(template_methods); |
| CPPUNIT_TEST_SUITE_END(); |
| |
| protected: |
| void uset(); |
| void umultiset(); |
| void umap(); |
| void umultimap(); |
| void user_case(); |
| void hash_policy(); |
| void buckets(); |
| void equal_range(); |
| void benchmark1(); |
| void benchmark2(); |
| void template_methods(); |
| }; |
| |
| CPPUNIT_TEST_SUITE_REGISTRATION(UnorderedTest); |
| |
| const int NB_ELEMS = 2000; |
| |
| // |
| // tests implementation |
| // |
| void UnorderedTest::uset() |
| { |
| #if defined (STLPORT) |
| typedef unordered_set<int, hash<int>, equal_to<int> > usettype; |
| usettype us; |
| |
| //Small compilation check of the copy constructor: |
| usettype us2(us); |
| //And assignment operator |
| us = us2; |
| |
| int i; |
| pair<usettype::iterator, bool> ret; |
| for (i = 0; i < NB_ELEMS; ++i) { |
| ret = us.insert(i); |
| CPPUNIT_ASSERT( ret.second ); |
| CPPUNIT_ASSERT( *ret.first == i ); |
| |
| ret = us.insert(i); |
| CPPUNIT_ASSERT( !ret.second ); |
| CPPUNIT_ASSERT( *ret.first == i ); |
| } |
| |
| vector<int> us_val; |
| |
| usettype::local_iterator lit, litEnd; |
| for (i = 0; i < NB_ELEMS; ++i) { |
| lit = us.begin(us.bucket(i)); |
| litEnd = us.end(us.bucket(i)); |
| |
| usettype::size_type bucket_pos = us.bucket(*lit); |
| for (; lit != litEnd; ++lit) { |
| CPPUNIT_ASSERT( us.bucket(*lit) == bucket_pos ); |
| us_val.push_back(*lit); |
| } |
| } |
| |
| //A compilation time check to uncomment from time to time |
| { |
| //usettype::iterator it; |
| //CPPUNIT_ASSERT( it != lit ); |
| } |
| |
| sort(us_val.begin(), us_val.end()); |
| for (i = 0; i < NB_ELEMS; ++i) { |
| CPPUNIT_ASSERT( us_val[i] == i ); |
| } |
| #endif |
| } |
| |
| void UnorderedTest::umultiset() |
| { |
| #if defined (STLPORT) |
| typedef unordered_multiset<int, hash<int>, equal_to<int> > usettype; |
| usettype us; |
| |
| int i; |
| usettype::iterator ret; |
| for (i = 0; i < NB_ELEMS; ++i) { |
| ret = us.insert(i); |
| CPPUNIT_ASSERT( *ret == i ); |
| |
| ret = us.insert(i); |
| CPPUNIT_ASSERT( *ret == i ); |
| } |
| |
| CPPUNIT_ASSERT( us.size() == 2 * NB_ELEMS ); |
| vector<int> us_val; |
| |
| usettype::local_iterator lit, litEnd; |
| for (i = 0; i < NB_ELEMS; ++i) { |
| lit = us.begin(us.bucket(i)); |
| litEnd = us.end(us.bucket(i)); |
| |
| usettype::size_type bucket_pos = us.bucket(*lit); |
| for (; lit != litEnd; ++lit) { |
| CPPUNIT_ASSERT( us.bucket(*lit) == bucket_pos ); |
| us_val.push_back(*lit); |
| } |
| } |
| |
| sort(us_val.begin(), us_val.end()); |
| for (i = 0; i < NB_ELEMS; ++i) { |
| CPPUNIT_ASSERT( us_val[2 * i] == i ); |
| CPPUNIT_ASSERT( us_val[2 * i + 1] == i ); |
| } |
| #endif |
| } |
| |
| void UnorderedTest::umap() |
| { |
| #if defined (STLPORT) |
| typedef unordered_map<int, int, hash<int>, equal_to<int> > umaptype; |
| umaptype us; |
| |
| //Compilation check of the [] operator: |
| umaptype us2; |
| us[0] = us2[0]; |
| us.clear(); |
| |
| { |
| //An other compilation check |
| typedef unordered_map<int, umaptype> uumaptype; |
| uumaptype uus; |
| umaptype const& uref = uus[0]; |
| umaptype ucopy = uus[0]; |
| ucopy = uref; |
| //Avoids warning: |
| //(void*)&uref; |
| } |
| |
| int i; |
| pair<umaptype::iterator, bool> ret; |
| for (i = 0; i < NB_ELEMS; ++i) { |
| umaptype::value_type p1(i, i); |
| ret = us.insert(p1); |
| CPPUNIT_ASSERT( ret.second ); |
| CPPUNIT_ASSERT( *ret.first == p1 ); |
| |
| umaptype::value_type p2(i, i + 1); |
| ret = us.insert(p2); |
| CPPUNIT_ASSERT( !ret.second ); |
| CPPUNIT_ASSERT( *ret.first == p1 ); |
| } |
| |
| { |
| //Lets look for some values to see if everything is normal: |
| umaptype::iterator umit; |
| for (int j = 0; j < NB_ELEMS; j += NB_ELEMS / 100) { |
| umit = us.find(j); |
| |
| CPPUNIT_ASSERT( umit != us.end() ); |
| CPPUNIT_ASSERT( (*umit).second == j ); |
| } |
| } |
| |
| CPPUNIT_ASSERT( us.size() == (size_t)NB_ELEMS ); |
| vector<pair<int, int> > us_val; |
| |
| umaptype::local_iterator lit, litEnd; |
| for (i = 0; i < NB_ELEMS; ++i) { |
| lit = us.begin(us.bucket(i)); |
| litEnd = us.end(us.bucket(i)); |
| |
| umaptype::size_type bucket_pos = us.bucket((*lit).first); |
| for (; lit != litEnd; ++lit) { |
| CPPUNIT_ASSERT( us.bucket((*lit).first) == bucket_pos ); |
| us_val.push_back(make_pair((*lit).first, (*lit).second)); |
| } |
| } |
| |
| sort(us_val.begin(), us_val.end()); |
| for (i = 0; i < NB_ELEMS; ++i) { |
| CPPUNIT_ASSERT( us_val[i] == make_pair(i, i) ); |
| } |
| #endif |
| } |
| |
| void UnorderedTest::umultimap() |
| { |
| #if defined (STLPORT) |
| typedef unordered_multimap<int, int, hash<int>, equal_to<int> > umaptype; |
| umaptype us; |
| |
| int i; |
| umaptype::iterator ret; |
| for (i = 0; i < NB_ELEMS; ++i) { |
| umaptype::value_type p(i, i); |
| ret = us.insert(p); |
| CPPUNIT_ASSERT( *ret == p ); |
| |
| ret = us.insert(p); |
| CPPUNIT_ASSERT( *ret == p ); |
| } |
| |
| CPPUNIT_ASSERT( us.size() == 2 * NB_ELEMS ); |
| typedef pair<int, int> ptype; |
| vector<ptype> us_val; |
| |
| umaptype::local_iterator lit, litEnd; |
| for (i = 0; i < NB_ELEMS; ++i) { |
| lit = us.begin(us.bucket(i)); |
| litEnd = us.end(us.bucket(i)); |
| |
| umaptype::size_type bucket_pos = us.bucket((*lit).first); |
| for (; lit != litEnd; ++lit) { |
| CPPUNIT_ASSERT( us.bucket((*lit).first) == bucket_pos ); |
| us_val.push_back(ptype((*lit).first, (*lit).second)); |
| } |
| } |
| |
| sort(us_val.begin(), us_val.end()); |
| for (i = 0; i < NB_ELEMS; ++i) { |
| ptype p(i, i); |
| CPPUNIT_ASSERT( us_val[i * 2] == p ); |
| CPPUNIT_ASSERT( us_val[i * 2 + 1] == p ); |
| } |
| #endif |
| } |
| |
| void UnorderedTest::user_case() |
| { |
| #if defined (STLPORT) |
| typedef unordered_map<int, string> UnorderedMap1; |
| typedef unordered_map<int, UnorderedMap1> UnorderedMap2; |
| |
| UnorderedMap1 foo; |
| UnorderedMap2 bar; |
| |
| foo.insert(UnorderedMap1::value_type(1, string("test1"))); |
| foo.insert(UnorderedMap1::value_type(2, string("test2"))); |
| foo.insert(UnorderedMap1::value_type(3, string("test3"))); |
| foo.insert(UnorderedMap1::value_type(4, string("test4"))); |
| foo.insert(UnorderedMap1::value_type(5, string("test5"))); |
| |
| bar.insert(UnorderedMap2::value_type(0, foo)); |
| UnorderedMap2::iterator it = bar.find(0); |
| CPPUNIT_ASSERT( it != bar.end() ); |
| |
| UnorderedMap1 &body = it->second; |
| UnorderedMap1::iterator cur = body.find(3); |
| CPPUNIT_ASSERT( cur != body.end() ); |
| |
| body.erase(body.begin(), body.end()); |
| CPPUNIT_ASSERT( body.empty() ); |
| #endif |
| } |
| |
| void UnorderedTest::hash_policy() |
| { |
| #if defined (STLPORT) |
| unordered_set<int> int_uset; |
| |
| CPPUNIT_ASSERT( int_uset.max_load_factor() == 1.0f ); |
| CPPUNIT_ASSERT( int_uset.load_factor() == 0.0f ); |
| |
| size_t nbInserts = int_uset.bucket_count() - 1; |
| for (int i = 0; (size_t)i < nbInserts; ++i) { |
| int_uset.insert(i); |
| } |
| CPPUNIT_ASSERT( int_uset.size() == nbInserts ); |
| |
| int_uset.max_load_factor(0.5f); |
| int_uset.rehash(0); |
| CPPUNIT_ASSERT( int_uset.load_factor() < int_uset.max_load_factor() ); |
| |
| size_t bucketsHint = int_uset.bucket_count() + 1; |
| int_uset.rehash(bucketsHint); |
| CPPUNIT_ASSERT( int_uset.bucket_count() >= bucketsHint ); |
| |
| CPPUNIT_ASSERT( int_uset.key_eq()(10, 10) ); |
| CPPUNIT_ASSERT( int_uset.hash_function()(10) == 10 ); |
| #endif |
| } |
| |
| void UnorderedTest::buckets() |
| { |
| #if defined (STLPORT) |
| unordered_set<int> int_uset; |
| |
| CPPUNIT_ASSERT( int_uset.bucket_count() < int_uset.max_bucket_count() ); |
| |
| int i; |
| size_t nbBuckets = int_uset.bucket_count(); |
| size_t nbInserts = int_uset.bucket_count() - 1; |
| for (i = 0; (size_t)i < nbInserts; ++i) { |
| int_uset.insert(i); |
| } |
| CPPUNIT_ASSERT( nbBuckets == int_uset.bucket_count() ); |
| |
| size_t bucketSizes = 0; |
| for (i = 0; (size_t)i < nbBuckets; ++i) { |
| bucketSizes += int_uset.bucket_size(i); |
| } |
| CPPUNIT_ASSERT( bucketSizes == int_uset.size() ); |
| #endif |
| } |
| |
| void UnorderedTest::equal_range() |
| { |
| #if defined (STLPORT) |
| typedef unordered_multiset<size_t> umset; |
| { |
| //General test |
| umset iumset; |
| iumset.max_load_factor(10.0f); |
| |
| size_t nbBuckets = iumset.bucket_count(); |
| |
| for (size_t i = 0; i < nbBuckets; ++i) { |
| iumset.insert(i); |
| iumset.insert(i + nbBuckets); |
| iumset.insert(i + 2 * nbBuckets); |
| iumset.insert(i + 3 * nbBuckets); |
| iumset.insert(i + 4 * nbBuckets); |
| } |
| |
| CPPUNIT_ASSERT( nbBuckets == iumset.bucket_count() ); |
| CPPUNIT_ASSERT( iumset.size() == 5 * nbBuckets ); |
| |
| pair<umset::iterator, umset::iterator> p = iumset.equal_range(1); |
| CPPUNIT_ASSERT( p.first != p.second ); |
| |
| size_t nbElems = iumset.size(); |
| nbElems -= distance(p.first, p.second); |
| for (umset::iterator j = p.first; j != p.second;) { |
| iumset.erase(j++); |
| } |
| |
| CPPUNIT_ASSERT( nbElems == iumset.size() ); |
| |
| p = iumset.equal_range(2); |
| CPPUNIT_ASSERT( p.first != p.second ); |
| nbElems -= distance(p.first, p.second); |
| iumset.erase(p.first, p.second); |
| CPPUNIT_ASSERT( nbElems == iumset.size() ); |
| } |
| |
| { |
| //More specific test that tries to put many values in the same bucket |
| umset iumset; |
| |
| size_t i; |
| //We are going to add at least 20 values, to get a stable hash container while doing that |
| //we force a large number of buckets: |
| iumset.rehash(193); |
| |
| size_t nbBuckets = iumset.bucket_count(); |
| const size_t targetedBucket = nbBuckets / 2; |
| |
| //Lets put 10 values in the targeted bucket: |
| for (i = 0; i < 10; ++i) { |
| iumset.insert(targetedBucket + (i * nbBuckets)); |
| } |
| |
| //We put again 10 values in the targeted bucket and in reverse order: |
| for (i = 9; i <= 10; --i) { |
| iumset.insert(targetedBucket + (i * nbBuckets)); |
| } |
| |
| //Now we put some more elements until hash container is resized: |
| i = 0; |
| while (iumset.bucket_count() == nbBuckets) { |
| iumset.insert(i++); |
| } |
| |
| //CPPUNIT_ASSERT( iumset.bucket_size(targetedBucket) == 21 ); |
| |
| pair<umset::iterator, umset::iterator> p = iumset.equal_range(targetedBucket); |
| CPPUNIT_ASSERT( p.first != p.second ); |
| CPPUNIT_ASSERT( distance(p.first, p.second) == 3 ); |
| |
| // Now we remove some elements until hash container is resized: |
| nbBuckets = iumset.bucket_count(); |
| while (iumset.bucket_count() == nbBuckets && |
| !iumset.empty()) { |
| iumset.erase(iumset.begin()); |
| } |
| CPPUNIT_ASSERT( iumset.load_factor() <= iumset.max_load_factor() ); |
| |
| // Adding an element back shouldn't change number of buckets: |
| nbBuckets = iumset.bucket_count(); |
| iumset.insert(0); |
| CPPUNIT_ASSERT( iumset.bucket_count() == nbBuckets ); |
| } |
| |
| { |
| srand(0); |
| for (int runs = 0; runs < 2; ++runs) { |
| size_t magic = rand(); |
| umset hum; |
| size_t c = 0; |
| for (int i = 0; i < 10000; ++i) { |
| if ((rand() % 500) == 0) { |
| hum.insert(magic); |
| ++c; |
| } |
| else { |
| size_t r; |
| while ((r = rand()) == magic) |
| ; |
| hum.insert(r); |
| } |
| |
| /* |
| if ((float)(hum.size() + 1) / (float)hum.bucket_count() > hum.max_load_factor()) { |
| cout << "Hash container dump: Nb elems: " << hum.size() << ", Nb buckets: " << hum.bucket_count() << "\n"; |
| for (size_t b = 0; b < hum.bucket_count(); ++b) { |
| if (hum.bucket_size(b) != 0) { |
| umset::local_iterator litBegin(hum.begin(b)), litEnd(hum.end(b)); |
| cout << "B" << b << ": "; |
| for (umset::local_iterator lit = litBegin; lit != litEnd; ++lit) { |
| if (lit != litBegin) { |
| cout << " - "; |
| } |
| cout << *lit; |
| } |
| cout << "\n"; |
| } |
| } |
| cout << endl; |
| } |
| */ |
| } |
| CPPUNIT_ASSERT( hum.count(magic) == c ); |
| } |
| } |
| #endif |
| } |
| |
| void UnorderedTest::benchmark1() |
| { |
| #if defined (STLPORT) |
| typedef unordered_multiset<size_t> umset; |
| |
| const size_t target = 500000; |
| umset iumset; |
| iumset.max_load_factor(10); |
| size_t i; |
| for (i = 0; i < target; ++i) { |
| iumset.insert(i); |
| } |
| |
| for (i = 0; i < target; ++i) { |
| iumset.erase(i); |
| } |
| #endif |
| } |
| |
| void UnorderedTest::benchmark2() |
| { |
| #if defined (STLPORT) |
| typedef unordered_multiset<size_t> umset; |
| |
| const size_t target = 500000; |
| umset iumset; |
| iumset.max_load_factor(10); |
| size_t i; |
| for (i = 0; i < target; ++i) { |
| iumset.insert(target - i); |
| } |
| |
| for (i = 0; i < target; ++i) { |
| iumset.erase(target - i); |
| } |
| #endif |
| } |
| |
| struct Key |
| { |
| Key() : m_data(0) {} |
| explicit Key(int data) : m_data(data) {} |
| |
| int m_data; |
| |
| #if defined (__DMC__) // slist<_Tp,_Alloc>::remove error |
| bool operator==(const Key&) const; |
| #endif |
| }; |
| |
| struct KeyHash |
| { |
| size_t operator () (Key key) const |
| { return (size_t)key.m_data; } |
| |
| size_t operator () (int data) const |
| { return (size_t)data; } |
| }; |
| |
| struct KeyEqual |
| { |
| bool operator () (Key lhs, Key rhs) const |
| { return lhs.m_data == rhs.m_data; } |
| |
| bool operator () (Key lhs, int rhs) const |
| { return lhs.m_data == rhs; } |
| |
| bool operator () (int lhs, Key rhs) const |
| { return lhs == rhs.m_data; } |
| }; |
| |
| struct KeyHashPtr |
| { |
| size_t operator () (Key const volatile *key) const |
| { return (size_t)key->m_data; } |
| |
| size_t operator () (int data) const |
| { return (size_t)data; } |
| }; |
| |
| struct KeyEqualPtr |
| { |
| bool operator () (Key const volatile *lhs, Key const volatile *rhs) const |
| { return lhs->m_data == rhs->m_data; } |
| |
| bool operator () (Key const volatile *lhs, int rhs) const |
| { return lhs->m_data == rhs; } |
| |
| bool operator () (int lhs, Key const volatile *rhs) const |
| { return lhs == rhs->m_data; } |
| }; |
| |
| void UnorderedTest::template_methods() |
| { |
| #if defined (STLPORT) && defined (_STLP_USE_CONTAINERS_EXTENSION) |
| { |
| typedef unordered_set<Key, KeyHash, KeyEqual> Container; |
| Container cont; |
| cont.insert(Key(1)); |
| cont.insert(Key(2)); |
| cont.insert(Key(3)); |
| cont.insert(Key(4)); |
| |
| CPPUNIT_ASSERT( cont.count(Key(1)) == 1 ); |
| CPPUNIT_ASSERT( cont.count(1) == 1 ); |
| CPPUNIT_ASSERT( cont.count(5) == 0 ); |
| |
| CPPUNIT_ASSERT( cont.find(2) != cont.end() ); |
| CPPUNIT_ASSERT( cont.equal_range(2) != make_pair(cont.begin(), cont.end()) ); |
| |
| Container const& ccont = cont; |
| CPPUNIT_ASSERT( ccont.find(2) != ccont.end() ); |
| CPPUNIT_ASSERT( ccont.bucket(2) == ccont.bucket(2) ); |
| CPPUNIT_ASSERT( ccont.equal_range(2) != make_pair(ccont.begin(), ccont.end()) ); |
| } |
| |
| { |
| typedef unordered_set<Key*, KeyHashPtr, KeyEqualPtr> Container; |
| Container cont; |
| Key key1(1), key2(2), key3(3), key4(4); |
| cont.insert(&key1); |
| cont.insert(&key2); |
| cont.insert(&key3); |
| cont.insert(&key4); |
| |
| CPPUNIT_ASSERT( cont.count(1) == 1 ); |
| CPPUNIT_ASSERT( cont.count(5) == 0 ); |
| |
| CPPUNIT_ASSERT( cont.find(2) != cont.end() ); |
| CPPUNIT_ASSERT( cont.equal_range(2) != make_pair(cont.begin(), cont.end()) ); |
| |
| Container const& ccont = cont; |
| CPPUNIT_ASSERT( ccont.find(2) != ccont.end() ); |
| CPPUNIT_ASSERT( ccont.bucket(2) == ccont.bucket(2) ); |
| CPPUNIT_ASSERT( ccont.equal_range(2) != make_pair(ccont.begin(), ccont.end()) ); |
| } |
| { |
| typedef unordered_multiset<Key, KeyHash, KeyEqual> Container; |
| Container cont; |
| cont.insert(Key(1)); |
| cont.insert(Key(2)); |
| cont.insert(Key(1)); |
| cont.insert(Key(2)); |
| |
| CPPUNIT_ASSERT( cont.count(Key(1)) == 2 ); |
| CPPUNIT_ASSERT( cont.count(1) == 2 ); |
| CPPUNIT_ASSERT( cont.count(5) == 0 ); |
| |
| CPPUNIT_ASSERT( cont.find(2) != cont.end() ); |
| CPPUNIT_ASSERT( cont.equal_range(1) != make_pair(cont.end(), cont.end()) ); |
| |
| Container const& ccont = cont; |
| CPPUNIT_ASSERT( ccont.find(2) != ccont.end() ); |
| CPPUNIT_ASSERT( ccont.bucket(2) == ccont.bucket(2) ); |
| CPPUNIT_ASSERT( ccont.equal_range(2) != make_pair(ccont.end(), ccont.end()) ); |
| } |
| |
| { |
| typedef unordered_multiset<Key const volatile*, KeyHashPtr, KeyEqualPtr> Container; |
| Container cont; |
| Key key1(1), key2(2), key3(3), key4(4); |
| cont.insert(&key1); |
| cont.insert(&key2); |
| cont.insert(&key3); |
| cont.insert(&key4); |
| |
| CPPUNIT_ASSERT( cont.count(1) == 1 ); |
| CPPUNIT_ASSERT( cont.count(5) == 0 ); |
| |
| CPPUNIT_ASSERT( cont.find(2) != cont.end() ); |
| CPPUNIT_ASSERT( cont.equal_range(2) != make_pair(cont.begin(), cont.end()) ); |
| |
| Container const& ccont = cont; |
| CPPUNIT_ASSERT( ccont.find(2) != ccont.end() ); |
| CPPUNIT_ASSERT( ccont.bucket(2) == ccont.bucket(2) ); |
| CPPUNIT_ASSERT( ccont.equal_range(2) != make_pair(ccont.begin(), ccont.end()) ); |
| } |
| #endif |
| } |
| |
| #if defined (STLPORT) && \ |
| (!defined (_STLP_USE_PTR_SPECIALIZATIONS) || defined (_STLP_CLASS_PARTIAL_SPECIALIZATION)) |
| # if !defined (__DMC__) |
| /* Simple compilation test: Check that nested types like iterator |
| * can be access even if type used to instanciate container is not |
| * yet completely defined. |
| */ |
| class IncompleteClass |
| { |
| unordered_set<IncompleteClass> usinstances; |
| typedef unordered_set<IncompleteClass>::iterator usit; |
| unordered_multiset<IncompleteClass> usminstances; |
| typedef unordered_multiset<IncompleteClass>::iterator usmit; |
| |
| unordered_map<IncompleteClass, IncompleteClass> uminstances; |
| typedef unordered_map<IncompleteClass, IncompleteClass>::iterator umit; |
| unordered_multimap<IncompleteClass, IncompleteClass> umminstances; |
| typedef unordered_multimap<IncompleteClass, IncompleteClass>::iterator ummit; |
| }; |
| # endif |
| #endif |