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).
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 >`]
12 [section:operators Operator Type Traits]
14 [heading Introduction]
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.
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:
28 It is also possible to know if the result of the operator can be used as function argument
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:
40 [heading Example of application]
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:
47 #include <boost/type_traits/has_plus_assign.hpp>
50 template < class Iterator, class Distance, bool has_plus_assign >
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) {
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) {
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);
80 Then the compiler chooses the most efficient implementation according to the type's ability to perform `+=` operation:
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; }
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; }
104 advance(i, 10); // uses +=
105 std::cout<<"with: "<<i<<'\n';
107 advance(j, 10); // uses ++
108 std::cout<<"without: "<<j<<'\n';
113 [heading Description]
114 The syntax is the following:
116 template __prefix_temp has_op; // prefix operator
117 template __postfix_temp has_op; // postfix operator
118 template __binary_temp has_op; // binary operator
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
128 The default behaviour (`Ret=dont_care`) is to not check for the return value of the
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`:
137 f(lhs+rhs); // is valid if has_plus<Lhs, Rhs, Ret>::value==true
139 If `Ret=void`, the return type is checked to be exactly `void`.
141 The following tables give the list of supported binary, prefix and postfix
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`]]]
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`]]]
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`]]]
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`.
197 [heading cv qualifiers and references]
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):
206 the following code also works (does not work):
213 It was not possible to handle properly the `volatile` qualifier so that any construct using this qualifier has undefined behavior.
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`.
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]]
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]]
236 [heading Implementation]
238 The implementation consists in only header files.
239 The following headers should included first:
240 ``#include <boost/type_traits/has_operator.hpp>``
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.
246 All traits are implemented the same way using preprocessor macros to avoid code
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:
256 // This namespace ensures that argument-dependent name lookup does not mess things up.
257 namespace has_unary_minus_impl {
259 // 1. a function to have an instance of type T without requiring T to be default
261 template <typename T> T &make();
264 // 2. we provide our operator definition for types that do not have one already
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 { };
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&); };
276 // when operator- is not available, this one is used
277 no_operator operator-(const any&);
280 // 3. checks if the operator returns void or not
281 // conditions: Rhs!=void
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);
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())));
305 // 4. checks if the return type is Ret or Ret==dont_care
306 // conditions: Rhs!=void
308 struct dont_care { };
310 template < typename Rhs, typename Ret, bool Returns_void >
311 struct operator_returns_Ret;
313 template < typename Rhs >
314 struct operator_returns_Ret < Rhs, dont_care, true > {
315 static const bool value = true;
318 template < typename Rhs >
319 struct operator_returns_Ret < Rhs, dont_care, false > {
320 static const bool value = true;
323 template < typename Rhs >
324 struct operator_returns_Ret < Rhs, void, true > {
325 static const bool value = true;
328 template < typename Rhs >
329 struct operator_returns_Ret < Rhs, void, false > {
330 static const bool value = false;
333 template < typename Rhs, typename Ret >
334 struct operator_returns_Ret < Rhs, Ret, true > {
335 static const bool value = false;
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
346 static const bool value = sizeof(is_convertible_to_Ret(-make<Rhs>()))==sizeof(::boost::type_traits::yes_type);
350 // 5. checks for operator existence
351 // condition: Rhs!=void
353 // checks if our definition of operator- is used or an other
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);
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
364 static const bool value = sizeof(check(((-make<Rhs>()),make<has_operator>())))==sizeof(::boost::type_traits::yes_type);
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)
374 template < typename Rhs, typename Ret, bool Forbidden_if >
377 template < typename Rhs, typename Ret >
378 struct trait_impl1 < Rhs, Ret, true > {
379 static const bool value = false;
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
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;
398 // defines some typedef for convenience
399 template < typename Rhs, typename Ret >
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;
408 } // namespace detail
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)> { };
419 * Requires a compiler with working SFINAE.
421 [heading Known issues]
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.
429 struct A { private: A operator-(); };
430 boost::has_unary_minus<A>::value; // error: A::operator-() is private
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.
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
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
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==`.
464 #include <boost/type_traits/has_equal_to.hpp>
468 struct contains { T data; };
471 bool operator==(const contains<T> &lhs, const contains<T> &rhs) {
472 return f(lhs.data, rhs.data);
477 bool f(const good&, const good&) { }
480 std::cout<<std::boolalpha;
481 // works fine for contains<good>
482 std::cout<<boost::has_equal_to< contains< good > >::value<<'\n'; // true
485 // does not work for contains<bad>
486 std::cout<<boost::has_equal_to< contains< bad > >::value<<'\n'; // true, should be false
488 b==b; // compile time error
493 * `volatile` qualifier is not properly handled and would lead to undefined behavior
496 [heading Acknowledgments]
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.