Brian Silverman | 4a2409e | 2018-08-04 23:24:02 -0700 | [diff] [blame^] | 1 | [/ |
| 2 | (C) Copyright 2011 Frederic Bron. |
| 3 | Distributed under the Boost Software License, Version 1.0. |
| 4 | (See accompanying file LICENSE_1_0.txt or copy at |
| 5 | http://www.boost.org/LICENSE_1_0.txt). |
| 6 | ] |
| 7 | [c++] |
| 8 | [def __binary_temp `< class Lhs, class Rhs=Lhs, class Ret=dont_care >`] |
| 9 | [def __prefix_temp `< class Rhs, class Ret=dont_care >`] |
| 10 | [def __postfix_temp `< class Lhs, class Ret=dont_care >`] |
| 11 | |
| 12 | [section:operators Operator Type Traits] |
| 13 | |
| 14 | [heading Introduction] |
| 15 | |
| 16 | These traits are all /value traits/ inheriting from __integral_constant |
| 17 | and providing a simple `true` or `false` boolean `value` which reflects the fact |
| 18 | that given types can or cannot be used with given operators. |
| 19 | |
| 20 | For example, `has_plus<int, double>::value` is a `bool` |
| 21 | which value is `true` because it is possible to add a `double` to an `int` like |
| 22 | in the following code: |
| 23 | `` |
| 24 | int i; |
| 25 | double d; |
| 26 | i+d; |
| 27 | `` |
| 28 | It is also possible to know if the result of the operator can be used as function argument |
| 29 | of a given type. |
| 30 | For example, `has_plus<int, double, float>::value` is `true` |
| 31 | because it is possible to add a `double` to an `int` and |
| 32 | the result (`double`) can be converted to a `float` argument like in the following code: |
| 33 | `` |
| 34 | void f(float) { }; |
| 35 | int i; |
| 36 | double d; |
| 37 | f(i+d); |
| 38 | `` |
| 39 | |
| 40 | [heading Example of application] |
| 41 | |
| 42 | These traits can be useful to optimize the code for types supporting given operations. |
| 43 | For example a function `std::advance` that increases an iterator of a given number of steps |
| 44 | could be implemented as follows: |
| 45 | |
| 46 | `` |
| 47 | #include <boost/type_traits/has_plus_assign.hpp> |
| 48 | |
| 49 | namespace detail { |
| 50 | template < class Iterator, class Distance, bool has_plus_assign > |
| 51 | struct advance_impl; |
| 52 | |
| 53 | // this is used if += exists (efficient) |
| 54 | template < class Iterator, class Distance > |
| 55 | struct advance_impl<Iterator, Distance, true> { |
| 56 | void operator()(Iterator &i, Distance n) { |
| 57 | i+=n; |
| 58 | } |
| 59 | }; |
| 60 | |
| 61 | // this is use if += does not exists (less efficient but cannot do better) |
| 62 | template < class Iterator, class Distance > |
| 63 | struct advance_impl<Iterator, Distance, false> { |
| 64 | void operator()(Iterator &i, Distance n) { |
| 65 | if (n>0) { |
| 66 | while (n--) ++i; |
| 67 | } else { |
| 68 | while (n++) --i; |
| 69 | } |
| 70 | } |
| 71 | }; |
| 72 | } // namespace detail |
| 73 | |
| 74 | template < class Iterator, class Distance > |
| 75 | inline void advance(Iterator &i, Distance n) { |
| 76 | detail::advance_impl< Iterator, Distance, ::boost::has_plus_assign<Iterator>::value >()(i, n); |
| 77 | } |
| 78 | `` |
| 79 | |
| 80 | Then the compiler chooses the most efficient implementation according to the type's ability to perform `+=` operation: |
| 81 | |
| 82 | `` |
| 83 | #include <iostream> |
| 84 | |
| 85 | class with { |
| 86 | int m_i; |
| 87 | public: |
| 88 | with(int i=0) : m_i(i) { } |
| 89 | with &operator+=(int rhs) { m_i+=rhs; return *this; } |
| 90 | operator int const () { return m_i; } |
| 91 | }; |
| 92 | |
| 93 | class without { |
| 94 | int m_i; |
| 95 | public: |
| 96 | without(int i=0) : m_i(i) { } |
| 97 | without &operator++() { ++m_i; return *this; } |
| 98 | without &operator--() { --m_i; return *this; } |
| 99 | operator int const () { return m_i; } |
| 100 | }; |
| 101 | |
| 102 | int main() { |
| 103 | with i=0; |
| 104 | advance(i, 10); // uses += |
| 105 | std::cout<<"with: "<<i<<'\n'; |
| 106 | without j=0; |
| 107 | advance(j, 10); // uses ++ |
| 108 | std::cout<<"without: "<<j<<'\n'; |
| 109 | return 0; |
| 110 | } |
| 111 | `` |
| 112 | |
| 113 | [heading Description] |
| 114 | The syntax is the following: |
| 115 | `` |
| 116 | template __prefix_temp has_op; // prefix operator |
| 117 | template __postfix_temp has_op; // postfix operator |
| 118 | template __binary_temp has_op; // binary operator |
| 119 | `` |
| 120 | where: |
| 121 | |
| 122 | * op represents the operator name |
| 123 | * `Lhs` is the type used at the left hand side of `operator op`, |
| 124 | * `Rhs` is the type used at the right hand side of `operator op`, |
| 125 | * `Ret` is the type for which we want to know if the result of `operator op` can |
| 126 | be converted to. |
| 127 | |
| 128 | The default behaviour (`Ret=dont_care`) is to not check for the return value of the |
| 129 | operator. |
| 130 | If `Ret` is different from the default `dont_care`, the return value is checked to be |
| 131 | convertible to `Ret`. Convertible to `Ret` means that the return value can be |
| 132 | used as argument to a function expecting `Ret`: |
| 133 | `` |
| 134 | void f(Ret); |
| 135 | Lhs lhs; |
| 136 | Rhs rhs; |
| 137 | f(lhs+rhs); // is valid if has_plus<Lhs, Rhs, Ret>::value==true |
| 138 | `` |
| 139 | If `Ret=void`, the return type is checked to be exactly `void`. |
| 140 | |
| 141 | The following tables give the list of supported binary, prefix and postfix |
| 142 | operators. |
| 143 | |
| 144 | [table Supported prefix operators |
| 145 | [[prefix operator] [trait name]] |
| 146 | [[`!`] [[link boost_typetraits.reference.has_logical_not `has_logical_not`] __prefix_temp]] |
| 147 | [[`+`] [[link boost_typetraits.reference.has_unary_plus `has_unary_plus`]]] |
| 148 | [[`-`] [[link boost_typetraits.reference.has_unary_minus `has_unary_minus`] and [link boost_typetraits.reference.has_negate `has_negate`]]] |
| 149 | [[`~`] [[link boost_typetraits.reference.has_complement `has_complement`]]] |
| 150 | [[`*`] [[link boost_typetraits.reference.has_dereference `has_dereference`]]] |
| 151 | [[`++`] [[link boost_typetraits.reference.has_pre_increment `has_pre_increment`]]] |
| 152 | [[`--`] [[link boost_typetraits.reference.has_pre_decrement `has_pre_decrement`]]] |
| 153 | ] |
| 154 | |
| 155 | [table Supported postfix operators |
| 156 | [[postfix operator] [trait name]] |
| 157 | [[`++`] [[link boost_typetraits.reference.has_post_increment `has_post_increment`] __postfix_temp]] |
| 158 | [[`--`] [[link boost_typetraits.reference.has_post_decrement `has_post_decrement`]]] |
| 159 | ] |
| 160 | |
| 161 | [table Supported binary operators |
| 162 | [[binary operator] [trait name]] |
| 163 | [[`+`] [[link boost_typetraits.reference.has_plus `has_plus`] __binary_temp]] |
| 164 | [[`-`] [[link boost_typetraits.reference.has_minus `has_minus`]]] |
| 165 | [[`*`] [[link boost_typetraits.reference.has_multiplies `has_multiplies`]]] |
| 166 | [[`/`] [[link boost_typetraits.reference.has_divides `has_divides`]]] |
| 167 | [[`%`] [[link boost_typetraits.reference.has_modulus `has_modulus`]]] |
| 168 | [[`+=`] [[link boost_typetraits.reference.has_plus_assign `has_plus_assign`]]] |
| 169 | [[`-=`] [[link boost_typetraits.reference.has_minus_assign `has_minus_assign`]]] |
| 170 | [[`*=`] [[link boost_typetraits.reference.has_multiplies_assign `has_multiplies_assign`]]] |
| 171 | [[`/=`] [[link boost_typetraits.reference.has_divides_assign `has_divides_assign`]]] |
| 172 | [[`%=`] [[link boost_typetraits.reference.has_modulus_assign `has_modulus_assign`]]] |
| 173 | [[`&`] [[link boost_typetraits.reference.has_bit_and `has_bit_and`]]] |
| 174 | [[`|`] [[link boost_typetraits.reference.has_bit_or `has_bit_or`]]] |
| 175 | [[`^`] [[link boost_typetraits.reference.has_bit_xor `has_bit_xor`]]] |
| 176 | [[`&=`] [[link boost_typetraits.reference.has_bit_and_assign `has_bit_and_assign`]]] |
| 177 | [[`|=`] [[link boost_typetraits.reference.has_bit_or_assign `has_bit_or_assign`]]] |
| 178 | [[`^=`] [[link boost_typetraits.reference.has_bit_xor_assign `has_bit_xor_assign`]]] |
| 179 | [[`<<`] [[link boost_typetraits.reference.has_left_shift `has_left_shift`]]] |
| 180 | [[`>>`] [[link boost_typetraits.reference.has_right_shift `has_right_shift`]]] |
| 181 | [[`<<=`] [[link boost_typetraits.reference.has_left_shift_assign `has_left_shift_assign`]]] |
| 182 | [[`>>=`] [[link boost_typetraits.reference.has_right_shift_assign `has_right_shift_assign`]]] |
| 183 | [[`==`] [[link boost_typetraits.reference.has_equal_to `has_equal_to`]]] |
| 184 | [[`!=`] [[link boost_typetraits.reference.has_not_equal_to `has_not_equal_to`]]] |
| 185 | [[`<`] [[link boost_typetraits.reference.has_less `has_less`]]] |
| 186 | [[`<=`] [[link boost_typetraits.reference.has_less_equal `has_less_equal`]]] |
| 187 | [[`>`] [[link boost_typetraits.reference.has_greater `has_greater`]]] |
| 188 | [[`>=`] [[link boost_typetraits.reference.has_greater_equal `has_greater_equal`]]] |
| 189 | [[`&&`] [[link boost_typetraits.reference.has_logical_and `has_logical_and`]]] |
| 190 | [[`||`] [[link boost_typetraits.reference.has_logical_or `has_logical_or`]]] |
| 191 | ] |
| 192 | |
| 193 | The following operators are not supported because they could not be implemented using the same technique: |
| 194 | `operator=`, `operator->`, `operator&`, `operator[]`, `operator,`, `operator()`, `operator new`. |
| 195 | |
| 196 | |
| 197 | [heading cv qualifiers and references] |
| 198 | |
| 199 | A reference sign `&` in the operator argument is ignored so that `has_plus< int&, double& >::value==has_plus< int, double >::value`. |
| 200 | This has been chosen because if the following code works (does not work): |
| 201 | `` |
| 202 | int i; |
| 203 | double d; |
| 204 | i+d; |
| 205 | `` |
| 206 | the following code also works (does not work): |
| 207 | `` |
| 208 | int &ir=i; |
| 209 | double &dr=d; |
| 210 | ir+dr; |
| 211 | `` |
| 212 | |
| 213 | It was not possible to handle properly the `volatile` qualifier so that any construct using this qualifier has undefined behavior. |
| 214 | |
| 215 | As a help, the following tables give the necessary conditions over each trait template argument for the trait `value` to be `true`. |
| 216 | They are non sufficient conditions because the conditions must be `true` for all arguments and return type for `value` to be `true`. |
| 217 | |
| 218 | [table necessary and non sufficient condition on operator argument for value to be true |
| 219 | [[operator declaration] [`has_op< void >`] [`has_op< Arg >` and `has_op< Arg& >`] [`has_op< Arg const >` and `has_op< Arg const& >`]] |
| 220 | [[`operator`@`(Arg)`] [false] [true] [true]] |
| 221 | [[`operator`@`(Arg const)`] [false] [true] [true]] |
| 222 | [[`operator`@`(Arg &)`] [false] [true] [false]] |
| 223 | [[`operator`@`(Arg const &)`] [false] [true] [true]] |
| 224 | ] |
| 225 | |
| 226 | [table necessary and non sufficient condition on operator return type for value to be true |
| 227 | [[operator declaration] [`has_op< ..., void >`] [`has_op< ..., Ret >`] [`has_op< ..., Ret const >`] [`has_op< ..., Ret & >`] [`has_op< ..., Ret const & >`]] |
| 228 | [[`void operator`@`(...)`] [true] [false] [false] [false] [false]] |
| 229 | [[`Ret operator`@`(...)`] [false] [true] [true] [false] [true]] |
| 230 | [[`Ret const operator`@`(...)`] [false] [true] [true] [false] [true]] |
| 231 | [[`Ret & operator`@`(...)`] [false] [true] [true] [true] [true]] |
| 232 | [[`Ret const & operator`@`(...)`] [false] [true] [true] [false] [true]] |
| 233 | ] |
| 234 | |
| 235 | |
| 236 | [heading Implementation] |
| 237 | |
| 238 | The implementation consists in only header files. |
| 239 | The following headers should included first: |
| 240 | ``#include <boost/type_traits/has_operator.hpp>`` |
| 241 | or |
| 242 | ``#include <boost/type_traits/has_op.hpp>`` |
| 243 | where [^op] is the textual name chosen for the wanted operator. |
| 244 | The first method includes all operator traits. |
| 245 | |
| 246 | All traits are implemented the same way using preprocessor macros to avoid code |
| 247 | duplication. |
| 248 | The main files are in [^boost/type_traits/detail]: [^has_binary_operator.hpp], |
| 249 | [^has_prefix_operator.hpp] and [^has_postfix_operator.hpp]. |
| 250 | The example of prefix `operator-` is presented below: |
| 251 | |
| 252 | `` |
| 253 | namespace boost { |
| 254 | namespace detail { |
| 255 | |
| 256 | // This namespace ensures that argument-dependent name lookup does not mess things up. |
| 257 | namespace has_unary_minus_impl { |
| 258 | |
| 259 | // 1. a function to have an instance of type T without requiring T to be default |
| 260 | // constructible |
| 261 | template <typename T> T &make(); |
| 262 | |
| 263 | |
| 264 | // 2. we provide our operator definition for types that do not have one already |
| 265 | |
| 266 | // a type returned from operator- when no such operator is |
| 267 | // found in the type's own namespace (our own operator is used) so that we have |
| 268 | // a means to know that our operator was used |
| 269 | struct no_operator { }; |
| 270 | |
| 271 | // this class allows implicit conversions and makes the following operator |
| 272 | // definition less-preferred than any other such operators that might be found |
| 273 | // via argument-dependent name lookup |
| 274 | struct any { template <class T> any(T const&); }; |
| 275 | |
| 276 | // when operator- is not available, this one is used |
| 277 | no_operator operator-(const any&); |
| 278 | |
| 279 | |
| 280 | // 3. checks if the operator returns void or not |
| 281 | // conditions: Rhs!=void |
| 282 | |
| 283 | // we first redefine "operator," so that we have no compilation error if |
| 284 | // operator- returns void and we can use the return type of |
| 285 | // (-rhs, returns_void_t()) to deduce if operator- returns void or not: |
| 286 | // - operator- returns void -> (-rhs, returns_void_t()) returns returns_void_t |
| 287 | // - operator- returns !=void -> (-rhs, returns_void_t()) returns int |
| 288 | struct returns_void_t { }; |
| 289 | template <typename T> int operator,(const T&, returns_void_t); |
| 290 | template <typename T> int operator,(const volatile T&, returns_void_t); |
| 291 | |
| 292 | // this intermediate trait has member value of type bool: |
| 293 | // - value==true -> operator- returns void |
| 294 | // - value==false -> operator- does not return void |
| 295 | template < typename Rhs > |
| 296 | struct operator_returns_void { |
| 297 | // overloads of function returns_void make the difference |
| 298 | // yes_type and no_type have different size by construction |
| 299 | static ::boost::type_traits::yes_type returns_void(returns_void_t); |
| 300 | static ::boost::type_traits::no_type returns_void(int); |
| 301 | static const bool value = sizeof(::boost::type_traits::yes_type)==sizeof(returns_void((-make<Rhs>(),returns_void_t()))); |
| 302 | }; |
| 303 | |
| 304 | |
| 305 | // 4. checks if the return type is Ret or Ret==dont_care |
| 306 | // conditions: Rhs!=void |
| 307 | |
| 308 | struct dont_care { }; |
| 309 | |
| 310 | template < typename Rhs, typename Ret, bool Returns_void > |
| 311 | struct operator_returns_Ret; |
| 312 | |
| 313 | template < typename Rhs > |
| 314 | struct operator_returns_Ret < Rhs, dont_care, true > { |
| 315 | static const bool value = true; |
| 316 | }; |
| 317 | |
| 318 | template < typename Rhs > |
| 319 | struct operator_returns_Ret < Rhs, dont_care, false > { |
| 320 | static const bool value = true; |
| 321 | }; |
| 322 | |
| 323 | template < typename Rhs > |
| 324 | struct operator_returns_Ret < Rhs, void, true > { |
| 325 | static const bool value = true; |
| 326 | }; |
| 327 | |
| 328 | template < typename Rhs > |
| 329 | struct operator_returns_Ret < Rhs, void, false > { |
| 330 | static const bool value = false; |
| 331 | }; |
| 332 | |
| 333 | template < typename Rhs, typename Ret > |
| 334 | struct operator_returns_Ret < Rhs, Ret, true > { |
| 335 | static const bool value = false; |
| 336 | }; |
| 337 | |
| 338 | // otherwise checks if it is convertible to Ret using the sizeof trick |
| 339 | // based on overload resolution |
| 340 | // condition: Ret!=void and Ret!=dont_care and the operator does not return void |
| 341 | template < typename Rhs, typename Ret > |
| 342 | struct operator_returns_Ret < Rhs, Ret, false > { |
| 343 | static ::boost::type_traits::yes_type is_convertible_to_Ret(Ret); // this version is preferred for types convertible to Ret |
| 344 | static ::boost::type_traits::no_type is_convertible_to_Ret(...); // this version is used otherwise |
| 345 | |
| 346 | static const bool value = sizeof(is_convertible_to_Ret(-make<Rhs>()))==sizeof(::boost::type_traits::yes_type); |
| 347 | }; |
| 348 | |
| 349 | |
| 350 | // 5. checks for operator existence |
| 351 | // condition: Rhs!=void |
| 352 | |
| 353 | // checks if our definition of operator- is used or an other |
| 354 | // existing one; |
| 355 | // this is done with redefinition of "operator," that returns no_operator or has_operator |
| 356 | struct has_operator { }; |
| 357 | no_operator operator,(no_operator, has_operator); |
| 358 | |
| 359 | template < typename Rhs > |
| 360 | struct operator_exists { |
| 361 | static ::boost::type_traits::yes_type check(has_operator); // this version is preferred when operator exists |
| 362 | static ::boost::type_traits::no_type check(no_operator); // this version is used otherwise |
| 363 | |
| 364 | static const bool value = sizeof(check(((-make<Rhs>()),make<has_operator>())))==sizeof(::boost::type_traits::yes_type); |
| 365 | }; |
| 366 | |
| 367 | |
| 368 | // 6. main trait: to avoid any compilation error, this class behaves |
| 369 | // differently when operator-(Rhs) is forbidden by the standard. |
| 370 | // Forbidden_if is a bool that is: |
| 371 | // - true when the operator-(Rhs) is forbidden by the standard |
| 372 | // (would yield compilation error if used) |
| 373 | // - false otherwise |
| 374 | template < typename Rhs, typename Ret, bool Forbidden_if > |
| 375 | struct trait_impl1; |
| 376 | |
| 377 | template < typename Rhs, typename Ret > |
| 378 | struct trait_impl1 < Rhs, Ret, true > { |
| 379 | static const bool value = false; |
| 380 | }; |
| 381 | |
| 382 | template < typename Rhs, typename Ret > |
| 383 | struct trait_impl1 < Rhs, Ret, false > { |
| 384 | static const bool value = |
| 385 | ::boost::type_traits::ice_and< |
| 386 | operator_exists < Rhs >::value, |
| 387 | operator_returns_Ret < Rhs, Ret, operator_returns_void < Rhs >::value >::value |
| 388 | >::value |
| 389 | ; |
| 390 | }; |
| 391 | |
| 392 | // specialization needs to be declared for the special void case |
| 393 | template < typename Ret > |
| 394 | struct trait_impl1 < void, Ret, false > { |
| 395 | static const bool value = false; |
| 396 | }; |
| 397 | |
| 398 | // defines some typedef for convenience |
| 399 | template < typename Rhs, typename Ret > |
| 400 | struct trait_impl { |
| 401 | typedef typename ::boost::remove_reference<Rhs>::type Rhs_noref; |
| 402 | typedef typename ::boost::remove_cv<Rhs_noref>::type Rhs_nocv; |
| 403 | typedef typename ::boost::remove_cv< typename ::boost::remove_reference< typename ::boost::remove_pointer<Rhs_noref>::type >::type >::type Rhs_noptr; |
| 404 | static const bool value = trait_impl1 < Rhs_noref, Ret, ::boost::is_pointer< Rhs_noref >::value >::value; |
| 405 | }; |
| 406 | |
| 407 | } // namespace impl |
| 408 | } // namespace detail |
| 409 | |
| 410 | // this is the accessible definition of the trait to end user |
| 411 | template < typename Rhs, typename Ret=::boost::detail::has_unary_minus_impl::dont_care > |
| 412 | struct has_unary_minus : ::boost::integral_constant<bool,(::boost::detail::has_unary_minus_impl::trait_impl < Rhs, Ret >::value)> { }; |
| 413 | |
| 414 | } // namespace boost |
| 415 | `` |
| 416 | |
| 417 | [heading Limitation] |
| 418 | |
| 419 | * Requires a compiler with working SFINAE. |
| 420 | |
| 421 | [heading Known issues] |
| 422 | |
| 423 | * These traits cannot detect whether the operators are public or not: |
| 424 | if an operator is defined as a private member of type `T` then |
| 425 | the instantiation of the corresponding trait will produce a compiler error. |
| 426 | For this reason these traits cannot be used to determine whether a type has a |
| 427 | public operator or not. |
| 428 | `` |
| 429 | struct A { private: A operator-(); }; |
| 430 | boost::has_unary_minus<A>::value; // error: A::operator-() is private |
| 431 | `` |
| 432 | |
| 433 | * There is an issue if the operator exists only for type `A` and `B` is |
| 434 | convertible to `A`. In this case, the compiler will report an ambiguous overload |
| 435 | because both the existing operator and the one we provide (with argument of type |
| 436 | `any`) need type conversion, so that none is preferred. |
| 437 | `` |
| 438 | struct A { }; |
| 439 | void operator-(const A&); |
| 440 | struct B { operator A(); }; |
| 441 | boost::has_unary_minus<A>::value; // this is fine |
| 442 | boost::has_unary_minus<B>::value; // error: ambiguous overload between |
| 443 | // operator-(const any&) and |
| 444 | // operator-(const A&) |
| 445 | // both need type conversion |
| 446 | `` |
| 447 | `` |
| 448 | struct B { }; |
| 449 | struct A { A(const B&) { } }; |
| 450 | void operator-(const A&); |
| 451 | boost::has_unary_minus<A>::value; // this is fine |
| 452 | boost::has_unary_minus<B>::value; // error: ambiguous overload between |
| 453 | // operator-(const any&) and |
| 454 | // operator-(const A&) |
| 455 | // both need type conversion |
| 456 | `` |
| 457 | |
| 458 | * There is an issue when applying these traits to template classes. |
| 459 | If the operator is defined but does not bind for a given template type, |
| 460 | it is still detected by the trait which returns `true` instead of `false`. |
| 461 | This applies in particular to the containers of the standard library and `operator==`. |
| 462 | Example: |
| 463 | `` |
| 464 | #include <boost/type_traits/has_equal_to.hpp> |
| 465 | #include <iostream> |
| 466 | |
| 467 | template <class T> |
| 468 | struct contains { T data; }; |
| 469 | |
| 470 | template <class T> |
| 471 | bool operator==(const contains<T> &lhs, const contains<T> &rhs) { |
| 472 | return f(lhs.data, rhs.data); |
| 473 | } |
| 474 | |
| 475 | class bad { }; |
| 476 | class good { }; |
| 477 | bool f(const good&, const good&) { } |
| 478 | |
| 479 | int main() { |
| 480 | std::cout<<std::boolalpha; |
| 481 | // works fine for contains<good> |
| 482 | std::cout<<boost::has_equal_to< contains< good > >::value<<'\n'; // true |
| 483 | contains<good> g; |
| 484 | g==g; // ok |
| 485 | // does not work for contains<bad> |
| 486 | std::cout<<boost::has_equal_to< contains< bad > >::value<<'\n'; // true, should be false |
| 487 | contains<bad> b; |
| 488 | b==b; // compile time error |
| 489 | return 0; |
| 490 | } |
| 491 | `` |
| 492 | |
| 493 | * `volatile` qualifier is not properly handled and would lead to undefined behavior |
| 494 | |
| 495 | |
| 496 | [heading Acknowledgments] |
| 497 | |
| 498 | Frederic Bron is very thankful to numerous people from the boost mailing list for their kind help and patience. |
| 499 | In particular, the following persons have been very helpful for the implementation: Edward Diener, Eric Niebler, Jeffrey Lee Hellrung (Jr.), Robert Stewart, Roman Perepelitsa, Steven Watanabe, Vicente Botet. |
| 500 | |
| 501 | [endsect] |
| 502 | |