]> git.proxmox.com Git - ceph.git/blame - ceph/src/boost/libs/type_traits/doc/operators.qbk
bump version to 12.2.2-pve1
[ceph.git] / ceph / src / boost / libs / type_traits / doc / operators.qbk
CommitLineData
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
16These traits are all /value traits/ inheriting from __integral_constant
17and providing a simple `true` or `false` boolean `value` which reflects the fact
18that given types can or cannot be used with given operators.
19
20For example, `has_plus<int, double>::value` is a `bool`
21which value is `true` because it is possible to add a `double` to an `int` like
22in the following code:
23``
24int i;
25double d;
26i+d;
27``
28It is also possible to know if the result of the operator can be used as function argument
29of a given type.
30For example, `has_plus<int, double, float>::value` is `true`
31because it is possible to add a `double` to an `int` and
32the result (`double`) can be converted to a `float` argument like in the following code:
33``
34void f(float) { };
35int i;
36double d;
37f(i+d);
38``
39
40[heading Example of application]
41
42These traits can be useful to optimize the code for types supporting given operations.
43For example a function `std::advance` that increases an iterator of a given number of steps
44could be implemented as follows:
45
46``
47#include <boost/type_traits/has_plus_assign.hpp>
48
49namespace detail {
50template < class Iterator, class Distance, bool has_plus_assign >
51struct advance_impl;
52
53// this is used if += exists (efficient)
54template < class Iterator, class Distance >
55struct 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)
62template < class Iterator, class Distance >
63struct 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
74template < class Iterator, class Distance >
75inline void advance(Iterator &i, Distance n) {
76 detail::advance_impl< Iterator, Distance, ::boost::has_plus_assign<Iterator>::value >()(i, n);
77}
78``
79
80Then the compiler chooses the most efficient implementation according to the type's ability to perform `+=` operation:
81
82``
83#include <iostream>
84
85class 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
93class 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
102int 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]
114The syntax is the following:
115``
116template __prefix_temp has_op; // prefix operator
117template __postfix_temp has_op; // postfix operator
118template __binary_temp has_op; // binary operator
119``
120where:
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
128The default behaviour (`Ret=dont_care`) is to not check for the return value of the
129operator.
130If `Ret` is different from the default `dont_care`, the return value is checked to be
131convertible to `Ret`. Convertible to `Ret` means that the return value can be
132used as argument to a function expecting `Ret`:
133``
134void f(Ret);
135Lhs lhs;
136Rhs rhs;
137f(lhs+rhs); // is valid if has_plus<Lhs, Rhs, Ret>::value==true
138``
139If `Ret=void`, the return type is checked to be exactly `void`.
140
141The following tables give the list of supported binary, prefix and postfix
142operators.
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
193The 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
199A reference sign `&` in the operator argument is ignored so that `has_plus< int&, double& >::value==has_plus< int, double >::value`.
200This has been chosen because if the following code works (does not work):
201``
202int i;
203double d;
204i+d;
205``
206the following code also works (does not work):
207``
208int &ir=i;
209double &dr=d;
210ir+dr;
211``
212
213It was not possible to handle properly the `volatile` qualifier so that any construct using this qualifier has undefined behavior.
214
215As a help, the following tables give the necessary conditions over each trait template argument for the trait `value` to be `true`.
216They 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
238The implementation consists in only header files.
239The following headers should included first:
240``#include <boost/type_traits/has_operator.hpp>``
241or
242``#include <boost/type_traits/has_op.hpp>``
243where [^op] is the textual name chosen for the wanted operator.
244The first method includes all operator traits.
245
246All traits are implemented the same way using preprocessor macros to avoid code
247duplication.
248The main files are in [^boost/type_traits/detail]: [^has_binary_operator.hpp],
249[^has_prefix_operator.hpp] and [^has_postfix_operator.hpp].
250The example of prefix `operator-` is presented below:
251
252``
253namespace boost {
254namespace detail {
255
256// This namespace ensures that argument-dependent name lookup does not mess things up.
257namespace has_unary_minus_impl {
258
259// 1. a function to have an instance of type T without requiring T to be default
260// constructible
261template <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
269struct 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
274struct any { template <class T> any(T const&); };
275
276// when operator- is not available, this one is used
277no_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
288struct returns_void_t { };
289template <typename T> int operator,(const T&, returns_void_t);
290template <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
295template < typename Rhs >
296struct 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
308struct dont_care { };
309
310template < typename Rhs, typename Ret, bool Returns_void >
311struct operator_returns_Ret;
312
313template < typename Rhs >
314struct operator_returns_Ret < Rhs, dont_care, true > {
315 static const bool value = true;
316};
317
318template < typename Rhs >
319struct operator_returns_Ret < Rhs, dont_care, false > {
320 static const bool value = true;
321};
322
323template < typename Rhs >
324struct operator_returns_Ret < Rhs, void, true > {
325 static const bool value = true;
326};
327
328template < typename Rhs >
329struct operator_returns_Ret < Rhs, void, false > {
330 static const bool value = false;
331};
332
333template < typename Rhs, typename Ret >
334struct 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
341template < typename Rhs, typename Ret >
342struct 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
356struct has_operator { };
357no_operator operator,(no_operator, has_operator);
358
359template < typename Rhs >
360struct 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
374template < typename Rhs, typename Ret, bool Forbidden_if >
375struct trait_impl1;
376
377template < typename Rhs, typename Ret >
378struct trait_impl1 < Rhs, Ret, true > {
379 static const bool value = false;
380};
381
382template < typename Rhs, typename Ret >
383struct 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
393template < typename Ret >
394struct trait_impl1 < void, Ret, false > {
395 static const bool value = false;
396};
397
398// defines some typedef for convenience
399template < typename Rhs, typename Ret >
400struct 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
411template < typename Rhs, typename Ret=::boost::detail::has_unary_minus_impl::dont_care >
412struct 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:
424if an operator is defined as a private member of type `T` then
425the instantiation of the corresponding trait will produce a compiler error.
426For this reason these traits cannot be used to determine whether a type has a
427public operator or not.
428``
429struct A { private: A operator-(); };
430boost::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
434convertible to `A`. In this case, the compiler will report an ambiguous overload
435because both the existing operator and the one we provide (with argument of type
436`any`) need type conversion, so that none is preferred.
437``
438struct A { };
439void operator-(const A&);
440struct B { operator A(); };
441boost::has_unary_minus<A>::value; // this is fine
442boost::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``
448struct B { };
449struct A { A(const B&) { } };
450void operator-(const A&);
451boost::has_unary_minus<A>::value; // this is fine
452boost::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.
459If the operator is defined but does not bind for a given template type,
460it is still detected by the trait which returns `true` instead of `false`.
461This applies in particular to the containers of the standard library and `operator==`.
462Example:
463``
464#include <boost/type_traits/has_equal_to.hpp>
465#include <iostream>
466
467template <class T>
468struct contains { T data; };
469
470template <class T>
471bool operator==(const contains<T> &lhs, const contains<T> &rhs) {
472 return f(lhs.data, rhs.data);
473}
474
475class bad { };
476class good { };
477bool f(const good&, const good&) { }
478
479int 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
498Frederic Bron is very thankful to numerous people from the boost mailing list for their kind help and patience.
499In 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