]>
Commit | Line | Data |
---|---|---|
7c673cae FG |
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 |