1 2 // Copyright 2006-2009 Daniel James. 3 // Distributed under the Boost Software License, Version 1.0. (See accompanying 4 // file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) 5 6 #if !defined(BOOST_UNORDERED_TEST_OBJECTS_HEADER) 7 #define BOOST_UNORDERED_TEST_OBJECTS_HEADER 8 9 #include "../helpers/count.hpp" 10 #include "../helpers/fwd.hpp" 11 #include "../helpers/memory.hpp" 12 #include <boost/config.hpp> 13 #include <boost/limits.hpp> 14 #include <cstddef> 15 16 namespace test { 17 // Note that the default hash function will work for any equal_to (but not 18 // very well). 19 class object; 20 class movable; 21 class implicitly_convertible; 22 class hash; 23 class less; 24 class equal_to; 25 template <class T> class allocator1; 26 template <class T> class allocator2; 27 object generate(object const*, random_generator); 28 movable generate(movable const*, random_generator); 29 implicitly_convertible generate( 30 implicitly_convertible const*, random_generator); 31 ignore_variable(void const *)32 inline void ignore_variable(void const*) {} 33 34 class object : private counted_object 35 { 36 friend class hash; 37 friend class equal_to; 38 friend class less; 39 int tag1_, tag2_; 40 41 public: object(int t1=0,int t2=0)42 explicit object(int t1 = 0, int t2 = 0) : tag1_(t1), tag2_(t2) {} 43 ~object()44 ~object() 45 { 46 tag1_ = -1; 47 tag2_ = -1; 48 } 49 operator ==(object const & x1,object const & x2)50 friend bool operator==(object const& x1, object const& x2) 51 { 52 return x1.tag1_ == x2.tag1_ && x1.tag2_ == x2.tag2_; 53 } 54 operator !=(object const & x1,object const & x2)55 friend bool operator!=(object const& x1, object const& x2) 56 { 57 return x1.tag1_ != x2.tag1_ || x1.tag2_ != x2.tag2_; 58 } 59 operator <(object const & x1,object const & x2)60 friend bool operator<(object const& x1, object const& x2) 61 { 62 return x1.tag1_ < x2.tag1_ || 63 (x1.tag1_ == x2.tag1_ && x1.tag2_ < x2.tag2_); 64 } 65 generate(object const *,random_generator g)66 friend object generate(object const*, random_generator g) 67 { 68 int* x = 0; 69 return object(generate(x, g), generate(x, g)); 70 } 71 operator <<(std::ostream & out,object const & o)72 friend std::ostream& operator<<(std::ostream& out, object const& o) 73 { 74 return out << "(" << o.tag1_ << "," << o.tag2_ << ")"; 75 } 76 }; 77 78 class movable : private counted_object 79 { 80 friend class hash; 81 friend class equal_to; 82 friend class less; 83 int tag1_, tag2_; 84 85 BOOST_COPYABLE_AND_MOVABLE(movable) 86 public: movable(int t1=0,int t2=0)87 explicit movable(int t1 = 0, int t2 = 0) : tag1_(t1), tag2_(t2) {} 88 movable(movable const & x)89 movable(movable const& x) 90 : counted_object(x), tag1_(x.tag1_), tag2_(x.tag2_) 91 { 92 BOOST_TEST(x.tag1_ != -1); 93 } 94 movable(BOOST_RV_REF (movable)x)95 movable(BOOST_RV_REF(movable) x) 96 : counted_object(x), tag1_(x.tag1_), tag2_(x.tag2_) 97 { 98 BOOST_TEST(x.tag1_ != -1); 99 x.tag1_ = -1; 100 x.tag2_ = -1; 101 } 102 operator =(BOOST_COPY_ASSIGN_REF (movable)x)103 movable& operator=(BOOST_COPY_ASSIGN_REF(movable) x) // Copy assignment 104 { 105 BOOST_TEST(x.tag1_ != -1); 106 tag1_ = x.tag1_; 107 tag2_ = x.tag2_; 108 return *this; 109 } 110 operator =(BOOST_RV_REF (movable)x)111 movable& operator=(BOOST_RV_REF(movable) x) // Move assignment 112 { 113 BOOST_TEST(x.tag1_ != -1); 114 tag1_ = x.tag1_; 115 tag2_ = x.tag2_; 116 x.tag1_ = -1; 117 x.tag2_ = -1; 118 return *this; 119 } 120 ~movable()121 ~movable() 122 { 123 tag1_ = -1; 124 tag2_ = -1; 125 } 126 operator ==(movable const & x1,movable const & x2)127 friend bool operator==(movable const& x1, movable const& x2) 128 { 129 BOOST_TEST(x1.tag1_ != -1 && x2.tag1_ != -1); 130 return x1.tag1_ == x2.tag1_ && x1.tag2_ == x2.tag2_; 131 } 132 operator !=(movable const & x1,movable const & x2)133 friend bool operator!=(movable const& x1, movable const& x2) 134 { 135 BOOST_TEST(x1.tag1_ != -1 && x2.tag1_ != -1); 136 return x1.tag1_ != x2.tag1_ || x1.tag2_ != x2.tag2_; 137 } 138 operator <(movable const & x1,movable const & x2)139 friend bool operator<(movable const& x1, movable const& x2) 140 { 141 BOOST_TEST(x1.tag1_ != -1 && x2.tag1_ != -1); 142 return x1.tag1_ < x2.tag1_ || 143 (x1.tag1_ == x2.tag1_ && x1.tag2_ < x2.tag2_); 144 } 145 generate(movable const *,random_generator g)146 friend movable generate(movable const*, random_generator g) 147 { 148 int* x = 0; 149 return movable(generate(x, g), generate(x, g)); 150 } 151 operator <<(std::ostream & out,movable const & o)152 friend std::ostream& operator<<(std::ostream& out, movable const& o) 153 { 154 return out << "(" << o.tag1_ << "," << o.tag2_ << ")"; 155 } 156 }; 157 158 class implicitly_convertible : private counted_object 159 { 160 int tag1_, tag2_; 161 162 public: implicitly_convertible(int t1=0,int t2=0)163 explicit implicitly_convertible(int t1 = 0, int t2 = 0) 164 : tag1_(t1), tag2_(t2) 165 { 166 } 167 operator object() const168 operator object() const { return object(tag1_, tag2_); } 169 operator movable() const170 operator movable() const { return movable(tag1_, tag2_); } 171 generate(implicitly_convertible const *,random_generator g)172 friend implicitly_convertible generate( 173 implicitly_convertible const*, random_generator g) 174 { 175 int* x = 0; 176 return implicitly_convertible(generate(x, g), generate(x, g)); 177 } 178 operator <<(std::ostream & out,implicitly_convertible const & o)179 friend std::ostream& operator<<( 180 std::ostream& out, implicitly_convertible const& o) 181 { 182 return out << "(" << o.tag1_ << "," << o.tag2_ << ")"; 183 } 184 }; 185 186 // Note: This is a deliberately bad hash function. 187 class hash 188 { 189 int type_; 190 191 public: hash()192 hash() : type_(0) {} 193 hash(int t)194 explicit hash(int t) : type_(t) {} 195 operator ()(object const & x) const196 std::size_t operator()(object const& x) const 197 { 198 int result; 199 switch (type_) { 200 case 1: 201 result = x.tag1_; 202 break; 203 case 2: 204 result = x.tag2_; 205 break; 206 default: 207 result = x.tag1_ + x.tag2_; 208 } 209 return static_cast<std::size_t>(result); 210 } 211 operator ()(movable const & x) const212 std::size_t operator()(movable const& x) const 213 { 214 int result; 215 switch (type_) { 216 case 1: 217 result = x.tag1_; 218 break; 219 case 2: 220 result = x.tag2_; 221 break; 222 default: 223 result = x.tag1_ + x.tag2_; 224 } 225 return static_cast<std::size_t>(result); 226 } 227 operator ()(int x) const228 std::size_t operator()(int x) const 229 { 230 int result; 231 switch (type_) { 232 case 1: 233 result = x; 234 break; 235 case 2: 236 result = x * 7; 237 break; 238 default: 239 result = x * 256; 240 } 241 return static_cast<std::size_t>(result); 242 } 243 operator ==(hash const & x1,hash const & x2)244 friend bool operator==(hash const& x1, hash const& x2) 245 { 246 return x1.type_ == x2.type_; 247 } 248 operator !=(hash const & x1,hash const & x2)249 friend bool operator!=(hash const& x1, hash const& x2) 250 { 251 return x1.type_ != x2.type_; 252 } 253 }; 254 hash_value(test::object const & x)255 std::size_t hash_value(test::object const& x) { return hash()(x); } 256 hash_value(test::movable const & x)257 std::size_t hash_value(test::movable const& x) { return hash()(x); } 258 259 class less 260 { 261 int type_; 262 263 public: less(int t=0)264 explicit less(int t = 0) : type_(t) {} 265 operator ()(object const & x1,object const & x2) const266 bool operator()(object const& x1, object const& x2) const 267 { 268 switch (type_) { 269 case 1: 270 return x1.tag1_ < x2.tag1_; 271 case 2: 272 return x1.tag2_ < x2.tag2_; 273 default: 274 return x1 < x2; 275 } 276 } 277 operator ()(movable const & x1,movable const & x2) const278 bool operator()(movable const& x1, movable const& x2) const 279 { 280 switch (type_) { 281 case 1: 282 return x1.tag1_ < x2.tag1_; 283 case 2: 284 return x1.tag2_ < x2.tag2_; 285 default: 286 return x1 < x2; 287 } 288 } 289 operator ()(int x1,int x2) const290 std::size_t operator()(int x1, int x2) const { return x1 < x2; } 291 operator ==(less const & x1,less const & x2)292 friend bool operator==(less const& x1, less const& x2) 293 { 294 return x1.type_ == x2.type_; 295 } 296 }; 297 298 class equal_to 299 { 300 int type_; 301 302 public: equal_to()303 equal_to() : type_(0) {} 304 equal_to(int t)305 explicit equal_to(int t) : type_(t) {} 306 operator ()(object const & x1,object const & x2) const307 bool operator()(object const& x1, object const& x2) const 308 { 309 switch (type_) { 310 case 1: 311 return x1.tag1_ == x2.tag1_; 312 case 2: 313 return x1.tag2_ == x2.tag2_; 314 default: 315 return x1 == x2; 316 } 317 } 318 operator ()(movable const & x1,movable const & x2) const319 bool operator()(movable const& x1, movable const& x2) const 320 { 321 switch (type_) { 322 case 1: 323 return x1.tag1_ == x2.tag1_; 324 case 2: 325 return x1.tag2_ == x2.tag2_; 326 default: 327 return x1 == x2; 328 } 329 } 330 operator ()(int x1,int x2) const331 std::size_t operator()(int x1, int x2) const { return x1 == x2; } 332 operator ==(equal_to const & x1,equal_to const & x2)333 friend bool operator==(equal_to const& x1, equal_to const& x2) 334 { 335 return x1.type_ == x2.type_; 336 } 337 operator !=(equal_to const & x1,equal_to const & x2)338 friend bool operator!=(equal_to const& x1, equal_to const& x2) 339 { 340 return x1.type_ != x2.type_; 341 } 342 create_compare(equal_to x)343 friend less create_compare(equal_to x) { return less(x.type_); } 344 }; 345 346 // allocator1 only has the old fashioned 'construct' method and has 347 // a few less typedefs. allocator2 uses a custom pointer class. 348 349 template <class T> class allocator1 350 { 351 public: 352 int tag_; 353 354 typedef T value_type; 355 356 template <class U> struct rebind 357 { 358 typedef allocator1<U> other; 359 }; 360 allocator1()361 allocator1() : tag_(0) { detail::tracker.allocator_ref(); } 362 allocator1(int t)363 explicit allocator1(int t) : tag_(t) { detail::tracker.allocator_ref(); } 364 allocator1(allocator1<Y> const & x)365 template <class Y> allocator1(allocator1<Y> const& x) : tag_(x.tag_) 366 { 367 detail::tracker.allocator_ref(); 368 } 369 allocator1(allocator1 const & x)370 allocator1(allocator1 const& x) : tag_(x.tag_) 371 { 372 detail::tracker.allocator_ref(); 373 } 374 ~allocator1()375 ~allocator1() { detail::tracker.allocator_unref(); } 376 allocate(std::size_t n)377 T* allocate(std::size_t n) 378 { 379 T* ptr(static_cast<T*>(::operator new(n * sizeof(T)))); 380 detail::tracker.track_allocate((void*)ptr, n, sizeof(T), tag_); 381 return ptr; 382 } 383 allocate(std::size_t n,void const *)384 T* allocate(std::size_t n, void const*) 385 { 386 T* ptr(static_cast<T*>(::operator new(n * sizeof(T)))); 387 detail::tracker.track_allocate((void*)ptr, n, sizeof(T), tag_); 388 return ptr; 389 } 390 deallocate(T * p,std::size_t n)391 void deallocate(T* p, std::size_t n) 392 { 393 detail::tracker.track_deallocate((void*)p, n, sizeof(T), tag_); 394 ::operator delete((void*)p); 395 } 396 397 #if BOOST_UNORDERED_CXX11_CONSTRUCTION construct(U * p,Args &&...args)398 template <typename U, typename... Args> void construct(U* p, Args&&... args) 399 { 400 detail::tracker.track_construct((void*)p, sizeof(U), tag_); 401 new (p) U(boost::forward<Args>(args)...); 402 } 403 destroy(U * p)404 template <typename U> void destroy(U* p) 405 { 406 detail::tracker.track_destroy((void*)p, sizeof(U), tag_); 407 p->~U(); 408 409 // Work around MSVC buggy unused parameter warning. 410 ignore_variable(&p); 411 } 412 #else 413 private: 414 // I'm going to claim in the documentation that construct/destroy 415 // is never used when C++11 support isn't available, so might as 416 // well check that in the text. 417 // TODO: Or maybe just disallow them for values? 418 template <typename U> void construct(U* p); 419 template <typename U, typename A0> void construct(U* p, A0 const&); 420 template <typename U, typename A0, typename A1> 421 void construct(U* p, A0 const&, A1 const&); 422 template <typename U, typename A0, typename A1, typename A2> 423 void construct(U* p, A0 const&, A1 const&, A2 const&); 424 template <typename U> void destroy(U* p); 425 426 public: 427 #endif 428 operator ==(allocator1 const & x) const429 bool operator==(allocator1 const& x) const { return tag_ == x.tag_; } 430 operator !=(allocator1 const & x) const431 bool operator!=(allocator1 const& x) const { return tag_ != x.tag_; } 432 433 enum 434 { 435 is_select_on_copy = false, 436 is_propagate_on_swap = false, 437 is_propagate_on_assign = false, 438 is_propagate_on_move = false 439 }; 440 }; 441 442 template <class T> class ptr; 443 template <class T> class const_ptr; 444 445 struct void_ptr 446 { 447 #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) 448 template <typename T> friend class ptr; 449 450 private: 451 #endif 452 453 void* ptr_; 454 455 public: void_ptrtest::void_ptr456 void_ptr() : ptr_(0) {} 457 void_ptrtest::void_ptr458 template <typename T> explicit void_ptr(ptr<T> const& x) : ptr_(x.ptr_) {} 459 460 // I'm not using the safe bool idiom because the containers should be 461 // able to cope with bool conversions. operator booltest::void_ptr462 operator bool() const { return !!ptr_; } 463 operator ==test::void_ptr464 bool operator==(void_ptr const& x) const { return ptr_ == x.ptr_; } operator !=test::void_ptr465 bool operator!=(void_ptr const& x) const { return ptr_ != x.ptr_; } 466 }; 467 468 class void_const_ptr 469 { 470 #if !defined(BOOST_NO_MEMBER_TEMPLATE_FRIENDS) 471 template <typename T> friend class const_ptr; 472 473 private: 474 #endif 475 476 void* ptr_; 477 478 public: void_const_ptr()479 void_const_ptr() : ptr_(0) {} 480 481 template <typename T> void_const_ptr(const_ptr<T> const & x)482 explicit void_const_ptr(const_ptr<T> const& x) : ptr_(x.ptr_) 483 { 484 } 485 486 // I'm not using the safe bool idiom because the containers should be 487 // able to cope with bool conversions. operator bool() const488 operator bool() const { return !!ptr_; } 489 operator ==(void_const_ptr const & x) const490 bool operator==(void_const_ptr const& x) const { return ptr_ == x.ptr_; } operator !=(void_const_ptr const & x) const491 bool operator!=(void_const_ptr const& x) const { return ptr_ != x.ptr_; } 492 }; 493 494 template <class T> class ptr 495 { 496 friend class allocator2<T>; 497 friend class const_ptr<T>; 498 friend struct void_ptr; 499 500 T* ptr_; 501 ptr(T * x)502 ptr(T* x) : ptr_(x) {} 503 504 public: ptr()505 ptr() : ptr_(0) {} ptr(void_ptr const & x)506 explicit ptr(void_ptr const& x) : ptr_((T*)x.ptr_) {} 507 operator *() const508 T& operator*() const { return *ptr_; } operator ->() const509 T* operator->() const { return ptr_; } operator ++()510 ptr& operator++() 511 { 512 ++ptr_; 513 return *this; 514 } operator ++(int)515 ptr operator++(int) 516 { 517 ptr tmp(*this); 518 ++ptr_; 519 return tmp; 520 } operator +(std::ptrdiff_t s) const521 ptr operator+(std::ptrdiff_t s) const { return ptr<T>(ptr_ + s); } operator +(std::ptrdiff_t s,ptr p)522 friend ptr operator+(std::ptrdiff_t s, ptr p) { return ptr<T>(s + p.ptr_); } operator [](std::ptrdiff_t s) const523 T& operator[](std::ptrdiff_t s) const { return ptr_[s]; } operator !() const524 bool operator!() const { return !ptr_; } 525 526 // I'm not using the safe bool idiom because the containers should be 527 // able to cope with bool conversions. operator bool() const528 operator bool() const { return !!ptr_; } 529 operator ==(ptr const & x) const530 bool operator==(ptr const& x) const { return ptr_ == x.ptr_; } operator !=(ptr const & x) const531 bool operator!=(ptr const& x) const { return ptr_ != x.ptr_; } operator <(ptr const & x) const532 bool operator<(ptr const& x) const { return ptr_ < x.ptr_; } operator >(ptr const & x) const533 bool operator>(ptr const& x) const { return ptr_ > x.ptr_; } operator <=(ptr const & x) const534 bool operator<=(ptr const& x) const { return ptr_ <= x.ptr_; } operator >=(ptr const & x) const535 bool operator>=(ptr const& x) const { return ptr_ >= x.ptr_; } 536 }; 537 538 template <class T> class const_ptr 539 { 540 friend class allocator2<T>; 541 friend struct const_void_ptr; 542 543 T const* ptr_; 544 const_ptr(T const * ptr)545 const_ptr(T const* ptr) : ptr_(ptr) {} 546 547 public: const_ptr()548 const_ptr() : ptr_(0) {} const_ptr(ptr<T> const & x)549 const_ptr(ptr<T> const& x) : ptr_(x.ptr_) {} const_ptr(void_const_ptr const & x)550 explicit const_ptr(void_const_ptr const& x) : ptr_((T const*)x.ptr_) {} 551 operator *() const552 T const& operator*() const { return *ptr_; } operator ->() const553 T const* operator->() const { return ptr_; } operator ++()554 const_ptr& operator++() 555 { 556 ++ptr_; 557 return *this; 558 } operator ++(int)559 const_ptr operator++(int) 560 { 561 const_ptr tmp(*this); 562 ++ptr_; 563 return tmp; 564 } operator +(std::ptrdiff_t s) const565 const_ptr operator+(std::ptrdiff_t s) const { return const_ptr(ptr_ + s); } operator +(std::ptrdiff_t s,const_ptr p)566 friend const_ptr operator+(std::ptrdiff_t s, const_ptr p) 567 { 568 return ptr<T>(s + p.ptr_); 569 } operator [](int s) const570 T const& operator[](int s) const { return ptr_[s]; } operator !() const571 bool operator!() const { return !ptr_; } operator bool() const572 operator bool() const { return !!ptr_; } 573 operator ==(const_ptr const & x) const574 bool operator==(const_ptr const& x) const { return ptr_ == x.ptr_; } operator !=(const_ptr const & x) const575 bool operator!=(const_ptr const& x) const { return ptr_ != x.ptr_; } operator <(const_ptr const & x) const576 bool operator<(const_ptr const& x) const { return ptr_ < x.ptr_; } operator >(const_ptr const & x) const577 bool operator>(const_ptr const& x) const { return ptr_ > x.ptr_; } operator <=(const_ptr const & x) const578 bool operator<=(const_ptr const& x) const { return ptr_ <= x.ptr_; } operator >=(const_ptr const & x) const579 bool operator>=(const_ptr const& x) const { return ptr_ >= x.ptr_; } 580 }; 581 582 template <class T> class allocator2 583 { 584 #ifdef BOOST_NO_MEMBER_TEMPLATE_FRIENDS 585 public: 586 #else 587 template <class> friend class allocator2; 588 #endif 589 int tag_; 590 591 public: 592 typedef std::size_t size_type; 593 typedef std::ptrdiff_t difference_type; 594 typedef void_ptr void_pointer; 595 typedef void_const_ptr const_void_pointer; 596 typedef ptr<T> pointer; 597 typedef const_ptr<T> const_pointer; 598 typedef T& reference; 599 typedef T const& const_reference; 600 typedef T value_type; 601 602 template <class U> struct rebind 603 { 604 typedef allocator2<U> other; 605 }; 606 allocator2()607 allocator2() : tag_(0) { detail::tracker.allocator_ref(); } 608 allocator2(int t)609 explicit allocator2(int t) : tag_(t) { detail::tracker.allocator_ref(); } 610 allocator2(allocator2<Y> const & x)611 template <class Y> allocator2(allocator2<Y> const& x) : tag_(x.tag_) 612 { 613 detail::tracker.allocator_ref(); 614 } 615 allocator2(allocator2 const & x)616 allocator2(allocator2 const& x) : tag_(x.tag_) 617 { 618 detail::tracker.allocator_ref(); 619 } 620 ~allocator2()621 ~allocator2() { detail::tracker.allocator_unref(); } 622 address(reference r)623 pointer address(reference r) { return pointer(&r); } 624 address(const_reference r)625 const_pointer address(const_reference r) { return const_pointer(&r); } 626 allocate(size_type n)627 pointer allocate(size_type n) 628 { 629 pointer p(static_cast<T*>(::operator new(n * sizeof(T)))); 630 detail::tracker.track_allocate((void*)p.ptr_, n, sizeof(T), tag_); 631 return p; 632 } 633 allocate(size_type n,void const *)634 pointer allocate(size_type n, void const*) 635 { 636 pointer ptr(static_cast<T*>(::operator new(n * sizeof(T)))); 637 detail::tracker.track_allocate((void*)ptr, n, sizeof(T), tag_); 638 return ptr; 639 } 640 deallocate(pointer p,size_type n)641 void deallocate(pointer p, size_type n) 642 { 643 detail::tracker.track_deallocate((void*)p.ptr_, n, sizeof(T), tag_); 644 ::operator delete((void*)p.ptr_); 645 } 646 construct(T * p,T const & t)647 void construct(T* p, T const& t) 648 { 649 detail::tracker.track_construct((void*)p, sizeof(T), tag_); 650 new (p) T(t); 651 } 652 653 #if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) construct(T * p,BOOST_FWD_REF (Args)...args)654 template <class... Args> void construct(T* p, BOOST_FWD_REF(Args)... args) 655 { 656 detail::tracker.track_construct((void*)p, sizeof(T), tag_); 657 new (p) T(boost::forward<Args>(args)...); 658 } 659 #endif 660 destroy(T * p)661 void destroy(T* p) 662 { 663 detail::tracker.track_destroy((void*)p, sizeof(T), tag_); 664 p->~T(); 665 } 666 max_size() const667 size_type max_size() const 668 { 669 return (std::numeric_limits<size_type>::max)(); 670 } 671 operator ==(allocator2 const & x) const672 bool operator==(allocator2 const& x) const { return tag_ == x.tag_; } 673 operator !=(allocator2 const & x) const674 bool operator!=(allocator2 const& x) const { return tag_ != x.tag_; } 675 676 enum 677 { 678 is_select_on_copy = false, 679 is_propagate_on_swap = false, 680 is_propagate_on_assign = false, 681 is_propagate_on_move = false 682 }; 683 }; 684 685 template <class T> equivalent_impl(allocator1<T> const & x,allocator1<T> const & y,test::derived_type)686 bool equivalent_impl( 687 allocator1<T> const& x, allocator1<T> const& y, test::derived_type) 688 { 689 return x == y; 690 } 691 692 template <class T> equivalent_impl(allocator2<T> const & x,allocator2<T> const & y,test::derived_type)693 bool equivalent_impl( 694 allocator2<T> const& x, allocator2<T> const& y, test::derived_type) 695 { 696 return x == y; 697 } 698 } 699 700 #endif 701