1 #include <vector>
2 #include <algorithm>
3 #include <string>
4 #if defined (STLPORT)
5 #  include <unordered_map>
6 #  include <unordered_set>
7 #endif
8 
9 //#include <iostream>
10 
11 #include "cppunit/cppunit_proxy.h"
12 
13 #if !defined (STLPORT) || defined(_STLP_USE_NAMESPACES)
14 using namespace std;
15 #  if defined (STLPORT)
16 using namespace std::tr1;
17 #  endif
18 #endif
19 
20 //
21 // TestCase class
22 //
23 class UnorderedTest : public CPPUNIT_NS::TestCase
24 {
25   CPPUNIT_TEST_SUITE(UnorderedTest);
26 #if !defined (STLPORT)
27   CPPUNIT_IGNORE;
28 #endif
29   CPPUNIT_TEST(uset);
30   CPPUNIT_TEST(umultiset);
31   CPPUNIT_TEST(umap);
32   CPPUNIT_TEST(umultimap);
33   CPPUNIT_TEST(user_case);
34   CPPUNIT_TEST(hash_policy);
35   CPPUNIT_TEST(buckets);
36   CPPUNIT_TEST(equal_range);
37   CPPUNIT_EXPLICIT_TEST(benchmark1);
38   CPPUNIT_EXPLICIT_TEST(benchmark2);
39 #if !defined (_STLP_USE_CONTAINERS_EXTENSION)
40   CPPUNIT_IGNORE;
41 #endif
42   CPPUNIT_TEST(template_methods);
43   CPPUNIT_TEST_SUITE_END();
44 
45 protected:
46   void uset();
47   void umultiset();
48   void umap();
49   void umultimap();
50   void user_case();
51   void hash_policy();
52   void buckets();
53   void equal_range();
54   void benchmark1();
55   void benchmark2();
56   void template_methods();
57 };
58 
59 CPPUNIT_TEST_SUITE_REGISTRATION(UnorderedTest);
60 
61 const int NB_ELEMS = 2000;
62 
63 //
64 // tests implementation
65 //
uset()66 void UnorderedTest::uset()
67 {
68 #if defined (STLPORT)
69   typedef unordered_set<int, hash<int>, equal_to<int> > usettype;
70   usettype us;
71 
72   //Small compilation check of the copy constructor:
73   usettype us2(us);
74   //And assignment operator
75   us = us2;
76 
77   int i;
78   pair<usettype::iterator, bool> ret;
79   for (i = 0; i < NB_ELEMS; ++i) {
80     ret = us.insert(i);
81     CPPUNIT_ASSERT( ret.second );
82     CPPUNIT_ASSERT( *ret.first == i );
83 
84     ret = us.insert(i);
85     CPPUNIT_ASSERT( !ret.second );
86     CPPUNIT_ASSERT( *ret.first == i );
87   }
88 
89   vector<int> us_val;
90 
91   usettype::local_iterator lit, litEnd;
92   for (i = 0; i < NB_ELEMS; ++i) {
93     lit = us.begin(us.bucket(i));
94     litEnd = us.end(us.bucket(i));
95 
96     usettype::size_type bucket_pos = us.bucket(*lit);
97     for (; lit != litEnd; ++lit) {
98       CPPUNIT_ASSERT( us.bucket(*lit) == bucket_pos );
99       us_val.push_back(*lit);
100     }
101   }
102 
103   //A compilation time check to uncomment from time to time
104   {
105     //usettype::iterator it;
106     //CPPUNIT_ASSERT( it != lit );
107   }
108 
109   sort(us_val.begin(), us_val.end());
110   for (i = 0; i < NB_ELEMS; ++i) {
111     CPPUNIT_ASSERT( us_val[i] == i );
112   }
113 #endif
114 }
115 
umultiset()116 void UnorderedTest::umultiset()
117 {
118 #if defined (STLPORT)
119   typedef unordered_multiset<int, hash<int>, equal_to<int> > usettype;
120   usettype us;
121 
122   int i;
123   usettype::iterator ret;
124   for (i = 0; i < NB_ELEMS; ++i) {
125     ret = us.insert(i);
126     CPPUNIT_ASSERT( *ret == i );
127 
128     ret = us.insert(i);
129     CPPUNIT_ASSERT( *ret == i );
130   }
131 
132   CPPUNIT_ASSERT( us.size() == 2 * NB_ELEMS );
133   vector<int> us_val;
134 
135   usettype::local_iterator lit, litEnd;
136   for (i = 0; i < NB_ELEMS; ++i) {
137     lit = us.begin(us.bucket(i));
138     litEnd = us.end(us.bucket(i));
139 
140     usettype::size_type bucket_pos = us.bucket(*lit);
141     for (; lit != litEnd; ++lit) {
142       CPPUNIT_ASSERT( us.bucket(*lit) == bucket_pos );
143       us_val.push_back(*lit);
144     }
145   }
146 
147   sort(us_val.begin(), us_val.end());
148   for (i = 0; i < NB_ELEMS; ++i) {
149     CPPUNIT_ASSERT( us_val[2 * i] == i );
150     CPPUNIT_ASSERT( us_val[2 * i + 1] == i );
151   }
152 #endif
153 }
154 
umap()155 void UnorderedTest::umap()
156 {
157 #if defined (STLPORT)
158   typedef unordered_map<int, int, hash<int>, equal_to<int> > umaptype;
159   umaptype us;
160 
161   //Compilation check of the [] operator:
162   umaptype us2;
163   us[0] = us2[0];
164   us.clear();
165 
166   {
167     //An other compilation check
168     typedef unordered_map<int, umaptype> uumaptype;
169     uumaptype uus;
170     umaptype const& uref = uus[0];
171     umaptype ucopy = uus[0];
172     ucopy = uref;
173     //Avoids warning:
174     //(void*)&uref;
175   }
176 
177   int i;
178   pair<umaptype::iterator, bool> ret;
179   for (i = 0; i < NB_ELEMS; ++i) {
180     umaptype::value_type p1(i, i);
181     ret = us.insert(p1);
182     CPPUNIT_ASSERT( ret.second );
183     CPPUNIT_ASSERT( *ret.first == p1 );
184 
185     umaptype::value_type p2(i, i + 1);
186     ret = us.insert(p2);
187     CPPUNIT_ASSERT( !ret.second );
188     CPPUNIT_ASSERT( *ret.first == p1 );
189   }
190 
191   {
192     //Lets look for some values to see if everything is normal:
193     umaptype::iterator umit;
194     for (int j = 0; j < NB_ELEMS; j += NB_ELEMS / 100) {
195       umit = us.find(j);
196 
197       CPPUNIT_ASSERT( umit != us.end() );
198       CPPUNIT_ASSERT( (*umit).second == j );
199     }
200   }
201 
202   CPPUNIT_ASSERT( us.size() == (size_t)NB_ELEMS );
203   vector<pair<int, int> > us_val;
204 
205   umaptype::local_iterator lit, litEnd;
206   for (i = 0; i < NB_ELEMS; ++i) {
207     lit = us.begin(us.bucket(i));
208     litEnd = us.end(us.bucket(i));
209 
210     umaptype::size_type bucket_pos = us.bucket((*lit).first);
211     for (; lit != litEnd; ++lit) {
212       CPPUNIT_ASSERT( us.bucket((*lit).first) == bucket_pos );
213       us_val.push_back(make_pair((*lit).first, (*lit).second));
214     }
215   }
216 
217   sort(us_val.begin(), us_val.end());
218   for (i = 0; i < NB_ELEMS; ++i) {
219     CPPUNIT_ASSERT( us_val[i] == make_pair(i, i) );
220   }
221 #endif
222 }
223 
umultimap()224 void UnorderedTest::umultimap()
225 {
226 #if defined (STLPORT)
227   typedef unordered_multimap<int, int, hash<int>, equal_to<int> > umaptype;
228   umaptype us;
229 
230   int i;
231   umaptype::iterator ret;
232   for (i = 0; i < NB_ELEMS; ++i) {
233     umaptype::value_type p(i, i);
234     ret = us.insert(p);
235     CPPUNIT_ASSERT( *ret == p );
236 
237     ret = us.insert(p);
238     CPPUNIT_ASSERT( *ret == p );
239   }
240 
241   CPPUNIT_ASSERT( us.size() == 2 * NB_ELEMS );
242   typedef pair<int, int> ptype;
243   vector<ptype> us_val;
244 
245   umaptype::local_iterator lit, litEnd;
246   for (i = 0; i < NB_ELEMS; ++i) {
247     lit = us.begin(us.bucket(i));
248     litEnd = us.end(us.bucket(i));
249 
250     umaptype::size_type bucket_pos = us.bucket((*lit).first);
251     for (; lit != litEnd; ++lit) {
252       CPPUNIT_ASSERT( us.bucket((*lit).first) == bucket_pos );
253       us_val.push_back(ptype((*lit).first, (*lit).second));
254     }
255   }
256 
257   sort(us_val.begin(), us_val.end());
258   for (i = 0; i < NB_ELEMS; ++i) {
259     ptype p(i, i);
260     CPPUNIT_ASSERT( us_val[i * 2] == p );
261     CPPUNIT_ASSERT( us_val[i * 2 + 1] == p );
262   }
263 #endif
264 }
265 
user_case()266 void UnorderedTest::user_case()
267 {
268 #if defined (STLPORT)
269   typedef unordered_map<int, string> UnorderedMap1;
270   typedef unordered_map<int, UnorderedMap1> UnorderedMap2;
271 
272   UnorderedMap1 foo;
273   UnorderedMap2 bar;
274 
275   foo.insert(UnorderedMap1::value_type(1, string("test1")));
276   foo.insert(UnorderedMap1::value_type(2, string("test2")));
277   foo.insert(UnorderedMap1::value_type(3, string("test3")));
278   foo.insert(UnorderedMap1::value_type(4, string("test4")));
279   foo.insert(UnorderedMap1::value_type(5, string("test5")));
280 
281   bar.insert(UnorderedMap2::value_type(0, foo));
282   UnorderedMap2::iterator it = bar.find(0);
283   CPPUNIT_ASSERT( it != bar.end() );
284 
285   UnorderedMap1 &body = it->second;
286   UnorderedMap1::iterator cur = body.find(3);
287   CPPUNIT_ASSERT( cur != body.end() );
288 
289   body.erase(body.begin(), body.end());
290   CPPUNIT_ASSERT( body.empty() );
291 #endif
292 }
293 
hash_policy()294 void UnorderedTest::hash_policy()
295 {
296 #if defined (STLPORT)
297   unordered_set<int> int_uset;
298 
299   CPPUNIT_ASSERT( int_uset.max_load_factor() == 1.0f );
300   CPPUNIT_ASSERT( int_uset.load_factor() == 0.0f );
301 
302   size_t nbInserts = int_uset.bucket_count() - 1;
303   for (int i = 0; (size_t)i < nbInserts; ++i) {
304     int_uset.insert(i);
305   }
306   CPPUNIT_ASSERT( int_uset.size() == nbInserts );
307 
308   int_uset.max_load_factor(0.5f);
309   int_uset.rehash(0);
310   CPPUNIT_ASSERT( int_uset.load_factor() < int_uset.max_load_factor() );
311 
312   size_t bucketsHint = int_uset.bucket_count() + 1;
313   int_uset.rehash(bucketsHint);
314   CPPUNIT_ASSERT( int_uset.bucket_count() >= bucketsHint );
315 
316   CPPUNIT_ASSERT( int_uset.key_eq()(10, 10) );
317   CPPUNIT_ASSERT( int_uset.hash_function()(10) == 10 );
318 #endif
319 }
320 
buckets()321 void UnorderedTest::buckets()
322 {
323 #if defined (STLPORT)
324   unordered_set<int> int_uset;
325 
326   CPPUNIT_ASSERT( int_uset.bucket_count() < int_uset.max_bucket_count() );
327 
328   int i;
329   size_t nbBuckets = int_uset.bucket_count();
330   size_t nbInserts = int_uset.bucket_count() - 1;
331   for (i = 0; (size_t)i < nbInserts; ++i) {
332     int_uset.insert(i);
333   }
334   CPPUNIT_ASSERT( nbBuckets == int_uset.bucket_count() );
335 
336   size_t bucketSizes = 0;
337   for (i = 0; (size_t)i < nbBuckets; ++i) {
338     bucketSizes += int_uset.bucket_size(i);
339   }
340   CPPUNIT_ASSERT( bucketSizes == int_uset.size() );
341 #endif
342 }
343 
equal_range()344 void UnorderedTest::equal_range()
345 {
346 #if defined (STLPORT)
347   typedef unordered_multiset<size_t> umset;
348   {
349     //General test
350     umset iumset;
351     iumset.max_load_factor(10.0f);
352 
353     size_t nbBuckets = iumset.bucket_count();
354 
355     for (size_t i = 0; i < nbBuckets; ++i) {
356       iumset.insert(i);
357       iumset.insert(i + nbBuckets);
358       iumset.insert(i + 2 * nbBuckets);
359       iumset.insert(i + 3 * nbBuckets);
360       iumset.insert(i + 4 * nbBuckets);
361     }
362 
363     CPPUNIT_ASSERT( nbBuckets == iumset.bucket_count() );
364     CPPUNIT_ASSERT( iumset.size() == 5 * nbBuckets );
365 
366     pair<umset::iterator, umset::iterator> p = iumset.equal_range(1);
367     CPPUNIT_ASSERT( p.first != p.second );
368 
369     size_t nbElems = iumset.size();
370     nbElems -= distance(p.first, p.second);
371     for (umset::iterator j = p.first; j != p.second;) {
372       iumset.erase(j++);
373     }
374 
375     CPPUNIT_ASSERT( nbElems == iumset.size() );
376 
377     p = iumset.equal_range(2);
378     CPPUNIT_ASSERT( p.first != p.second );
379     nbElems -= distance(p.first, p.second);
380     iumset.erase(p.first, p.second);
381     CPPUNIT_ASSERT( nbElems == iumset.size() );
382   }
383 
384   {
385     //More specific test that tries to put many values in the same bucket
386     umset iumset;
387 
388     size_t i;
389     //We are going to add at least 20 values, to get a stable hash container while doing that
390     //we force a large number of buckets:
391     iumset.rehash(193);
392 
393     size_t nbBuckets = iumset.bucket_count();
394     const size_t targetedBucket = nbBuckets / 2;
395 
396     //Lets put 10 values in the targeted bucket:
397     for (i = 0; i < 10; ++i) {
398       iumset.insert(targetedBucket + (i * nbBuckets));
399     }
400 
401     //We put again 10 values in the targeted bucket and in reverse order:
402     for (i = 9; i <= 10; --i) {
403       iumset.insert(targetedBucket + (i * nbBuckets));
404     }
405 
406     //Now we put some more elements until hash container is resized:
407     i = 0;
408     while (iumset.bucket_count() == nbBuckets) {
409       iumset.insert(i++);
410     }
411 
412     //CPPUNIT_ASSERT( iumset.bucket_size(targetedBucket) == 21 );
413 
414     pair<umset::iterator, umset::iterator> p = iumset.equal_range(targetedBucket);
415     CPPUNIT_ASSERT( p.first != p.second );
416     CPPUNIT_ASSERT( distance(p.first, p.second) == 3 );
417 
418     // Now we remove some elements until hash container is resized:
419     nbBuckets = iumset.bucket_count();
420     while (iumset.bucket_count() == nbBuckets &&
421            !iumset.empty()) {
422       iumset.erase(iumset.begin());
423     }
424     CPPUNIT_ASSERT( iumset.load_factor() <= iumset.max_load_factor() );
425 
426     // Adding an element back shouldn't change number of buckets:
427     nbBuckets = iumset.bucket_count();
428     iumset.insert(0);
429     CPPUNIT_ASSERT( iumset.bucket_count() == nbBuckets );
430   }
431 
432   {
433     srand(0);
434     for (int runs = 0; runs < 2; ++runs) {
435       size_t magic = rand();
436       umset hum;
437       size_t c = 0;
438       for (int i = 0; i < 10000; ++i) {
439         if ((rand() % 500) == 0) {
440           hum.insert(magic);
441           ++c;
442         }
443         else {
444           size_t r;
445           while ((r = rand()) == magic)
446             ;
447           hum.insert(r);
448         }
449 
450         /*
451         if ((float)(hum.size() + 1) / (float)hum.bucket_count() > hum.max_load_factor()) {
452           cout << "Hash container dump: Nb elems: " << hum.size() << ", Nb buckets: " << hum.bucket_count() << "\n";
453           for (size_t b = 0; b < hum.bucket_count(); ++b) {
454             if (hum.bucket_size(b) != 0) {
455               umset::local_iterator litBegin(hum.begin(b)), litEnd(hum.end(b));
456               cout << "B" << b << ": ";
457               for (umset::local_iterator lit = litBegin; lit != litEnd; ++lit) {
458                 if (lit != litBegin) {
459                   cout << " - ";
460                 }
461                 cout << *lit;
462               }
463               cout << "\n";
464             }
465           }
466           cout << endl;
467         }
468         */
469       }
470       CPPUNIT_ASSERT( hum.count(magic) == c );
471     }
472   }
473 #endif
474 }
475 
benchmark1()476 void UnorderedTest::benchmark1()
477 {
478 #if defined (STLPORT)
479   typedef unordered_multiset<size_t> umset;
480 
481   const size_t target = 500000;
482   umset iumset;
483   iumset.max_load_factor(10);
484   size_t i;
485   for (i = 0; i < target; ++i) {
486     iumset.insert(i);
487   }
488 
489   for (i = 0; i < target; ++i) {
490     iumset.erase(i);
491   }
492 #endif
493 }
494 
benchmark2()495 void UnorderedTest::benchmark2()
496 {
497 #if defined (STLPORT)
498   typedef unordered_multiset<size_t> umset;
499 
500   const size_t target = 500000;
501   umset iumset;
502   iumset.max_load_factor(10);
503   size_t i;
504   for (i = 0; i < target; ++i) {
505     iumset.insert(target - i);
506   }
507 
508   for (i = 0; i < target; ++i) {
509     iumset.erase(target - i);
510   }
511 #endif
512 }
513 
514 struct Key
515 {
KeyKey516   Key() : m_data(0) {}
KeyKey517   explicit Key(int data) : m_data(data) {}
518 
519   int m_data;
520 
521 #if defined (__DMC__) // slist<_Tp,_Alloc>::remove error
522   bool operator==(const Key&) const;
523 #endif
524 };
525 
526 struct KeyHash
527 {
operator ()KeyHash528   size_t operator () (Key key) const
529   { return (size_t)key.m_data; }
530 
operator ()KeyHash531   size_t operator () (int data) const
532   { return (size_t)data; }
533 };
534 
535 struct KeyEqual
536 {
operator ()KeyEqual537   bool operator () (Key lhs, Key rhs) const
538   { return lhs.m_data == rhs.m_data; }
539 
operator ()KeyEqual540   bool operator () (Key lhs, int rhs) const
541   { return lhs.m_data == rhs; }
542 
operator ()KeyEqual543   bool operator () (int lhs, Key rhs) const
544   { return lhs == rhs.m_data; }
545 };
546 
547 struct KeyHashPtr
548 {
operator ()KeyHashPtr549   size_t operator () (Key const volatile *key) const
550   { return (size_t)key->m_data; }
551 
operator ()KeyHashPtr552   size_t operator () (int data) const
553   { return (size_t)data; }
554 };
555 
556 struct KeyEqualPtr
557 {
operator ()KeyEqualPtr558   bool operator () (Key const volatile *lhs, Key const volatile *rhs) const
559   { return lhs->m_data == rhs->m_data; }
560 
operator ()KeyEqualPtr561   bool operator () (Key const volatile *lhs, int rhs) const
562   { return lhs->m_data == rhs; }
563 
operator ()KeyEqualPtr564   bool operator () (int lhs, Key const volatile *rhs) const
565   { return lhs == rhs->m_data; }
566 };
567 
template_methods()568 void UnorderedTest::template_methods()
569 {
570 #if defined (STLPORT) && defined (_STLP_USE_CONTAINERS_EXTENSION)
571   {
572     typedef unordered_set<Key, KeyHash, KeyEqual> Container;
573     Container cont;
574     cont.insert(Key(1));
575     cont.insert(Key(2));
576     cont.insert(Key(3));
577     cont.insert(Key(4));
578 
579     CPPUNIT_ASSERT( cont.count(Key(1)) == 1 );
580     CPPUNIT_ASSERT( cont.count(1) == 1 );
581     CPPUNIT_ASSERT( cont.count(5) == 0 );
582 
583     CPPUNIT_ASSERT( cont.find(2) != cont.end() );
584     CPPUNIT_ASSERT( cont.equal_range(2) != make_pair(cont.begin(), cont.end()) );
585 
586     Container const& ccont = cont;
587     CPPUNIT_ASSERT( ccont.find(2) != ccont.end() );
588     CPPUNIT_ASSERT( ccont.bucket(2) == ccont.bucket(2) );
589     CPPUNIT_ASSERT( ccont.equal_range(2) != make_pair(ccont.begin(), ccont.end()) );
590   }
591 
592   {
593     typedef unordered_set<Key*, KeyHashPtr, KeyEqualPtr> Container;
594     Container cont;
595     Key key1(1), key2(2), key3(3), key4(4);
596     cont.insert(&key1);
597     cont.insert(&key2);
598     cont.insert(&key3);
599     cont.insert(&key4);
600 
601     CPPUNIT_ASSERT( cont.count(1) == 1 );
602     CPPUNIT_ASSERT( cont.count(5) == 0 );
603 
604     CPPUNIT_ASSERT( cont.find(2) != cont.end() );
605     CPPUNIT_ASSERT( cont.equal_range(2) != make_pair(cont.begin(), cont.end()) );
606 
607     Container const& ccont = cont;
608     CPPUNIT_ASSERT( ccont.find(2) != ccont.end() );
609     CPPUNIT_ASSERT( ccont.bucket(2) == ccont.bucket(2) );
610     CPPUNIT_ASSERT( ccont.equal_range(2) != make_pair(ccont.begin(), ccont.end()) );
611   }
612   {
613     typedef unordered_multiset<Key, KeyHash, KeyEqual> Container;
614     Container cont;
615     cont.insert(Key(1));
616     cont.insert(Key(2));
617     cont.insert(Key(1));
618     cont.insert(Key(2));
619 
620     CPPUNIT_ASSERT( cont.count(Key(1)) == 2 );
621     CPPUNIT_ASSERT( cont.count(1) == 2 );
622     CPPUNIT_ASSERT( cont.count(5) == 0 );
623 
624     CPPUNIT_ASSERT( cont.find(2) != cont.end() );
625     CPPUNIT_ASSERT( cont.equal_range(1) != make_pair(cont.end(), cont.end()) );
626 
627     Container const& ccont = cont;
628     CPPUNIT_ASSERT( ccont.find(2) != ccont.end() );
629     CPPUNIT_ASSERT( ccont.bucket(2) == ccont.bucket(2) );
630     CPPUNIT_ASSERT( ccont.equal_range(2) != make_pair(ccont.end(), ccont.end()) );
631   }
632 
633   {
634     typedef unordered_multiset<Key const volatile*, KeyHashPtr, KeyEqualPtr> Container;
635     Container cont;
636     Key key1(1), key2(2), key3(3), key4(4);
637     cont.insert(&key1);
638     cont.insert(&key2);
639     cont.insert(&key3);
640     cont.insert(&key4);
641 
642     CPPUNIT_ASSERT( cont.count(1) == 1 );
643     CPPUNIT_ASSERT( cont.count(5) == 0 );
644 
645     CPPUNIT_ASSERT( cont.find(2) != cont.end() );
646     CPPUNIT_ASSERT( cont.equal_range(2) != make_pair(cont.begin(), cont.end()) );
647 
648     Container const& ccont = cont;
649     CPPUNIT_ASSERT( ccont.find(2) != ccont.end() );
650     CPPUNIT_ASSERT( ccont.bucket(2) == ccont.bucket(2) );
651     CPPUNIT_ASSERT( ccont.equal_range(2) != make_pair(ccont.begin(), ccont.end()) );
652   }
653 #endif
654 }
655 
656 #if defined (STLPORT) && \
657     (!defined (_STLP_USE_PTR_SPECIALIZATIONS) || defined (_STLP_CLASS_PARTIAL_SPECIALIZATION))
658 #  if !defined (__DMC__)
659 /* Simple compilation test: Check that nested types like iterator
660  * can be access even if type used to instanciate container is not
661  * yet completely defined.
662  */
663 class IncompleteClass
664 {
665   unordered_set<IncompleteClass> usinstances;
666   typedef unordered_set<IncompleteClass>::iterator usit;
667   unordered_multiset<IncompleteClass> usminstances;
668   typedef unordered_multiset<IncompleteClass>::iterator usmit;
669 
670   unordered_map<IncompleteClass, IncompleteClass> uminstances;
671   typedef unordered_map<IncompleteClass, IncompleteClass>::iterator umit;
672   unordered_multimap<IncompleteClass, IncompleteClass> umminstances;
673   typedef unordered_multimap<IncompleteClass, IncompleteClass>::iterator ummit;
674 };
675 #  endif
676 #endif
677