1 /* Copyright 2009-2016 Francesco Biscani (bluescarni@gmail.com)
2 
3 This file is part of the Piranha library.
4 
5 The Piranha library is free software; you can redistribute it and/or modify
6 it under the terms of either:
7 
8   * the GNU Lesser General Public License as published by the Free
9     Software Foundation; either version 3 of the License, or (at your
10     option) any later version.
11 
12 or
13 
14   * the GNU General Public License as published by the Free Software
15     Foundation; either version 3 of the License, or (at your option) any
16     later version.
17 
18 or both in parallel, as here.
19 
20 The Piranha library is distributed in the hope that it will be useful, but
21 WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
22 or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
23 for more details.
24 
25 You should have received copies of the GNU General Public License and the
26 GNU Lesser General Public License along with the Piranha library.  If not,
27 see https://www.gnu.org/licenses/. */
28 
29 #ifndef PIRANHA_TYPE_TRAITS_HPP
30 #define PIRANHA_TYPE_TRAITS_HPP
31 
32 /** \file type_traits.hpp
33  * \brief Type traits.
34  *
35  * This header contains general-purpose type traits classes.
36  */
37 
38 #include <cstdarg>
39 #include <cstddef>
40 #include <functional>
41 #include <initializer_list>
42 #include <iterator>
43 #include <limits>
44 #include <ostream>
45 #include <tuple>
46 #include <type_traits>
47 #include <utility>
48 
49 #include "config.hpp"
50 #include "detail/sfinae_types.hpp"
51 
52 namespace piranha
53 {
54 
55 inline namespace impl
56 {
57 
58 // http://en.cppreference.com/w/cpp/types/void_t
59 template <typename... Ts>
60 struct make_void {
61     typedef void type;
62 };
63 
64 template <typename... Ts>
65 using void_t = typename make_void<Ts...>::type;
66 
67 // http://en.cppreference.com/w/cpp/experimental/is_detected
68 template <class Default, class AlwaysVoid, template <class...> class Op, class... Args>
69 struct detector {
70     using value_t = std::false_type;
71     using type = Default;
72 };
73 
74 template <class Default, template <class...> class Op, class... Args>
75 struct detector<Default, void_t<Op<Args...>>, Op, Args...> {
76     using value_t = std::true_type;
77     using type = Op<Args...>;
78 };
79 
80 // http://en.cppreference.com/w/cpp/experimental/nonesuch
81 struct nonesuch {
82     nonesuch() = delete;
83     ~nonesuch() = delete;
84     nonesuch(nonesuch const &) = delete;
85     void operator=(nonesuch const &) = delete;
86 };
87 
88 template <template <class...> class Op, class... Args>
89 using is_detected = typename detector<nonesuch, void, Op, Args...>::value_t;
90 
91 template <template <class...> class Op, class... Args>
92 using detected_t = typename detector<nonesuch, void, Op, Args...>::type;
93 
94 // http://en.cppreference.com/w/cpp/types/conjunction
95 template <class...>
96 struct conjunction : std::true_type {
97 };
98 
99 template <class B1>
100 struct conjunction<B1> : B1 {
101 };
102 
103 template <class B1, class... Bn>
104 struct conjunction<B1, Bn...> : std::conditional<B1::value != false, conjunction<Bn...>, B1>::type {
105 };
106 
107 // http://en.cppreference.com/w/cpp/types/disjunction
108 template <class...>
109 struct disjunction : std::false_type {
110 };
111 
112 template <class B1>
113 struct disjunction<B1> : B1 {
114 };
115 
116 template <class B1, class... Bn>
117 struct disjunction<B1, Bn...> : std::conditional<B1::value != false, B1, disjunction<Bn...>>::type {
118 };
119 
120 // http://en.cppreference.com/w/cpp/types/negation
121 template <class B>
122 struct negation : std::integral_constant<bool, !B::value> {
123 };
124 
125 // std::index_sequence and std::make_index_sequence implementation for C++11. These are available
126 // in the std library in C++14. Implementation taken from:
127 // http://stackoverflow.com/questions/17424477/implementation-c14-make-integer-sequence
128 template <std::size_t... Ints>
129 struct index_sequence {
130     using type = index_sequence;
131     using value_type = std::size_t;
sizepiranha::impl::index_sequence132     static constexpr std::size_t size() noexcept
133     {
134         return sizeof...(Ints);
135     }
136 };
137 
138 template <class Sequence1, class Sequence2>
139 struct merge_and_renumber;
140 
141 template <std::size_t... I1, std::size_t... I2>
142 struct merge_and_renumber<index_sequence<I1...>, index_sequence<I2...>>
143     : index_sequence<I1..., (sizeof...(I1) + I2)...> {
144 };
145 
146 template <std::size_t N>
147 struct make_index_sequence
148     : merge_and_renumber<typename make_index_sequence<N / 2>::type, typename make_index_sequence<N - N / 2>::type> {
149 };
150 
151 template <>
152 struct make_index_sequence<0> : index_sequence<> {
153 };
154 
155 template <>
156 struct make_index_sequence<1> : index_sequence<0> {
157 };
158 
159 template <typename T, typename F, std::size_t... Is>
apply_to_each_item(T && t,const F & f,index_sequence<Is...>)160 void apply_to_each_item(T &&t, const F &f, index_sequence<Is...>)
161 {
162     (void)std::initializer_list<int>{0, (void(f(std::get<Is>(std::forward<T>(t)))), 0)...};
163 }
164 
165 // Tuple for_each(). Execute the functor f on each element of the input Tuple.
166 // https://isocpp.org/blog/2015/01/for-each-arg-eric-niebler
167 // https://www.reddit.com/r/cpp/comments/2tffv3/for_each_argumentsean_parent/
168 // https://www.reddit.com/r/cpp/comments/33b06v/for_each_in_tuple/
169 template <class Tuple, class F>
tuple_for_each(Tuple && t,const F & f)170 void tuple_for_each(Tuple &&t, const F &f)
171 {
172     apply_to_each_item(std::forward<Tuple>(t), f,
173                        make_index_sequence<std::tuple_size<typename std::decay<Tuple>::type>::value>{});
174 }
175 
176 // Some handy aliases.
177 template <typename T>
178 using uncvref_t = typename std::remove_cv<typename std::remove_reference<T>::type>::type;
179 
180 template <typename T>
181 using decay_t = typename std::decay<T>::type;
182 
183 template <typename T>
184 using unref_t = typename std::remove_reference<T>::type;
185 
186 template <typename T>
187 using addlref_t = typename std::add_lvalue_reference<T>::type;
188 
189 template <bool B, typename T = void>
190 using enable_if_t = typename std::enable_if<B, T>::type;
191 
192 template <typename T>
193 using is_nonconst_rvalue_ref
194     = std::integral_constant<bool, std::is_rvalue_reference<T>::value
195                                        && !std::is_const<typename std::remove_reference<T>::type>::value>;
196 
197 template <typename T, typename U, typename Derived>
198 struct arith_tt_helper {
199     static const bool value = std::is_same<decltype(Derived::test(*(unref_t<T> *)nullptr, *(unref_t<U> *)nullptr)),
200                                            detail::sfinae_types::yes>::value;
201 };
202 }
203 
204 /// Addable type trait.
205 /**
206  * Will be \p true if objects of type \p T can be added to objects of type \p U using the binary addition operator.
207  *
208  * This type trait will strip \p T and \p U of reference qualifiers, and it will test the operator in the form
209  @code
210  operator+(const Td &, const Ud &)
211  @endcode
212  * where \p Td and \p Ud are \p T and \p U after the removal of reference qualifiers. E.g.:
213  @code
214  is_addable<int>::value == true;
215  is_addable<int,std::string>::value == false;
216  @endcode
217  */
218 template <typename T, typename U = T>
219 class is_addable : detail::sfinae_types
220 {
221     friend struct arith_tt_helper<T, U, is_addable<T, U>>;
222     template <typename T1, typename U1>
223     static auto test(const T1 &t, const U1 &u) -> decltype(t + u, void(), yes());
224     static no test(...);
225 
226 public:
227     /// Value of the type trait.
228     static const bool value = arith_tt_helper<T, U, is_addable>::value;
229 };
230 
231 template <typename T, typename U>
232 const bool is_addable<T, U>::value;
233 
234 /// In-place addable type trait.
235 /**
236  * Will be \p true if objects of type \p U can be added in-place to objects of type \p T.
237  *
238  * This type trait will strip \p T and \p U of reference qualifiers, and it will test the operator in the form
239  @code
240  operator+=(Td &, const Ud &)
241  @endcode
242  * where \p Td and \p Ud are \p T and \p U after the removal of reference qualifiers. E.g.:
243  @code
244  is_addable_in_place<int>::value == true;
245  is_addable_in_place<int,std::string>::value == false;
246  @endcode
247  */
248 template <typename T, typename U = T>
249 class is_addable_in_place : detail::sfinae_types
250 {
251     friend struct arith_tt_helper<T, U, is_addable_in_place<T, U>>;
252     template <typename T1, typename U1>
253     static auto test(T1 &t, const U1 &u) -> decltype(t += u, void(), yes());
254     static no test(...);
255 
256 public:
257     /// Value of the type trait.
258     static const bool value = arith_tt_helper<T, U, is_addable_in_place>::value;
259 };
260 
261 template <typename T, typename U>
262 const bool is_addable_in_place<T, U>::value;
263 
264 /// Subtractable type trait.
265 /**
266  * @see piranha::is_addable.
267  */
268 template <typename T, typename U = T>
269 class is_subtractable : detail::sfinae_types
270 {
271     friend struct arith_tt_helper<T, U, is_subtractable<T, U>>;
272     template <typename T1, typename U1>
273     static auto test(const T1 &t, const U1 &u) -> decltype(t - u, void(), yes());
274     static no test(...);
275 
276 public:
277     /// Value of the type trait.
278     static const bool value = arith_tt_helper<T, U, is_subtractable>::value;
279 };
280 
281 template <typename T, typename U>
282 const bool is_subtractable<T, U>::value;
283 
284 /// In-place subtractable type trait.
285 /**
286  * @see piranha::is_addable_in_place.
287  */
288 template <typename T, typename U = T>
289 class is_subtractable_in_place : detail::sfinae_types
290 {
291     friend struct arith_tt_helper<T, U, is_subtractable_in_place<T, U>>;
292     template <typename T1, typename U1>
293     static auto test(T1 &t, const U1 &u) -> decltype(t -= u, void(), yes());
294     static no test(...);
295 
296 public:
297     /// Value of the type trait.
298     static const bool value = arith_tt_helper<T, U, is_subtractable_in_place>::value;
299 };
300 
301 template <typename T, typename U>
302 const bool is_subtractable_in_place<T, U>::value;
303 
304 /// Multipliable type trait.
305 /**
306  * @see piranha::is_addable.
307  */
308 template <typename T, typename U = T>
309 class is_multipliable : detail::sfinae_types
310 {
311     friend struct arith_tt_helper<T, U, is_multipliable<T, U>>;
312     template <typename T1, typename U1>
313     static auto test(const T1 &t, const U1 &u) -> decltype(t * u, void(), yes());
314     static no test(...);
315 
316 public:
317     /// Value of the type trait.
318     static const bool value = arith_tt_helper<T, U, is_multipliable>::value;
319 };
320 
321 template <typename T, typename U>
322 const bool is_multipliable<T, U>::value;
323 
324 /// In-place multipliable type trait.
325 /**
326  * @see piranha::is_addable_in_place.
327  */
328 template <typename T, typename U = T>
329 class is_multipliable_in_place : detail::sfinae_types
330 {
331     friend struct arith_tt_helper<T, U, is_multipliable_in_place<T, U>>;
332     template <typename T1, typename U1>
333     static auto test(T1 &t, const U1 &u) -> decltype(t *= u, void(), yes());
334     static no test(...);
335 
336 public:
337     /// Value of the type trait.
338     static const bool value = arith_tt_helper<T, U, is_multipliable_in_place>::value;
339 };
340 
341 template <typename T, typename U>
342 const bool is_multipliable_in_place<T, U>::value;
343 
344 /// Divisible type trait.
345 /**
346  * @see piranha::is_addable.
347  */
348 template <typename T, typename U = T>
349 class is_divisible : detail::sfinae_types
350 {
351     friend struct arith_tt_helper<T, U, is_divisible<T, U>>;
352     template <typename T1, typename U1>
353     static auto test(const T1 &t, const U1 &u) -> decltype(t / u, void(), yes());
354     static no test(...);
355 
356 public:
357     /// Value of the type trait.
358     static const bool value = arith_tt_helper<T, U, is_divisible>::value;
359 };
360 
361 template <typename T, typename U>
362 const bool is_divisible<T, U>::value;
363 
364 /// In-place divisible type trait.
365 /**
366  * @see piranha::is_addable_in_place.
367  */
368 template <typename T, typename U = T>
369 class is_divisible_in_place : detail::sfinae_types
370 {
371     friend struct arith_tt_helper<T, U, is_divisible_in_place<T, U>>;
372     template <typename T1, typename U1>
373     static auto test(T1 &t, const U1 &u) -> decltype(t /= u, void(), yes());
374     static no test(...);
375 
376 public:
377     /// Value of the type trait.
378     static const bool value = arith_tt_helper<T, U, is_divisible_in_place>::value;
379 };
380 
381 template <typename T, typename U>
382 const bool is_divisible_in_place<T, U>::value;
383 
384 /// Equality-comparable type trait.
385 /**
386  * This type trait is \p true if instances if type \p T can be compared for equality and inequality to instances of
387  * type \p U. The operators must be non-mutable (i.e., implemented using pass-by-value or const
388  * references) and must return a type implicitly convertible to \p bool.
389  */
390 template <typename T, typename U = T>
391 class is_equality_comparable : detail::sfinae_types
392 {
393     typedef typename std::decay<T>::type Td;
394     typedef typename std::decay<U>::type Ud;
395     template <typename T1, typename U1>
396     static auto test1(const T1 &t, const U1 &u) -> decltype(t == u);
397     static no test1(...);
398     template <typename T1, typename U1>
399     static auto test2(const T1 &t, const U1 &u) -> decltype(t != u);
400     static no test2(...);
401     static const bool implementation_defined
402         = std::is_convertible<decltype(test1(std::declval<Td>(), std::declval<Ud>())), bool>::value
403           && std::is_convertible<decltype(test2(std::declval<Td>(), std::declval<Ud>())), bool>::value;
404 
405 public:
406     /// Value of the type trait.
407     static const bool value = implementation_defined;
408 };
409 
410 // Static init.
411 template <typename T, typename U>
412 const bool is_equality_comparable<T, U>::value;
413 
414 /// Less-than-comparable type trait.
415 /**
416  * This type trait is \p true if instances of type \p T can be compared to instances of
417  * type \p U using the less-than operator. The operator must be non-mutable (i.e., implemented using pass-by-value or
418  * const
419  * references) and must return a type implicitly convertible to \p bool.
420  */
421 template <typename T, typename U = T>
422 class is_less_than_comparable : detail::sfinae_types
423 {
424     typedef typename std::decay<T>::type Td;
425     typedef typename std::decay<U>::type Ud;
426     template <typename T1, typename U1>
427     static auto test(const T1 &t, const U1 &u) -> decltype(t < u);
428     static no test(...);
429 
430 public:
431     /// Value of the type trait.
432     static const bool value = std::is_convertible<decltype(test(std::declval<Td>(), std::declval<Ud>())), bool>::value;
433 };
434 
435 // Static init.
436 template <typename T, typename U>
437 const bool is_less_than_comparable<T, U>::value;
438 
439 /// Greater-than-comparable type trait.
440 /**
441  * This type trait is \p true if instances of type \p T can be compared to instances of
442  * type \p U using the greater-than operator. The operator must be non-mutable (i.e., implemented using pass-by-value or
443  * const
444  * references) and must return a type implicitly convertible to \p bool.
445  */
446 template <typename T, typename U = T>
447 class is_greater_than_comparable : detail::sfinae_types
448 {
449     typedef typename std::decay<T>::type Td;
450     typedef typename std::decay<U>::type Ud;
451     template <typename T1, typename U1>
452     static auto test(const T1 &t, const U1 &u) -> decltype(t > u);
453     static no test(...);
454 
455 public:
456     /// Value of the type trait.
457     static const bool value = std::is_convertible<decltype(test(std::declval<Td>(), std::declval<Ud>())), bool>::value;
458 };
459 
460 // Static init.
461 template <typename T, typename U>
462 const bool is_greater_than_comparable<T, U>::value;
463 
464 /// Enable \p noexcept checks.
465 /**
466  * This type trait, to be specialised with the <tt>std::enable_if</tt> mechanism, enables or disables
467  * \p noexcept checks in the piranha::is_container_element type trait. This type trait should be used
468  * only with legacy pre-C++11 classes that do not support \p noexcept: by specialising this trait to
469  * \p false, the piranha::is_container_element trait will disable \p noexcept checks and it will thus be
470  * possible to use \p noexcept unaware classes as container elements and series coefficients.
471  */
472 template <typename T, typename = void>
473 struct enable_noexcept_checks {
474     /// Default value of the type trait.
475     static const bool value = true;
476 };
477 
478 // Static init.
479 template <typename T, typename Enable>
480 const bool enable_noexcept_checks<T, Enable>::value;
481 
482 /// Type trait for well-behaved container elements.
483 /**
484  * The type trait will be true if all these conditions hold:
485  *
486  * - \p T is default-constructible,
487  * - \p T is copy-constructible,
488  * - \p T has nothrow move semantics,
489  * - \p T is nothrow-destructible.
490  *
491  * If the piranha::enable_noexcept_checks trait is \p false for \p T, then the nothrow checks will be discarded.
492  */
493 template <typename T>
494 struct is_container_element {
495     // NOTE: here we do not require copy assignability as in our containers we always implement
496     // copy-assign as copy-construct + move for exception safety reasons.
497     /// Value of the type trait.
498     static const bool value = std::is_default_constructible<T>::value && std::is_copy_constructible<T>::value
499                               && (!enable_noexcept_checks<T>::value
500                                   || (std::is_nothrow_destructible<T>::value
501 // The Intel compiler has troubles with the noexcept versions of these two type traits.
502 #if defined(PIRANHA_COMPILER_IS_INTEL)
503                                       && std::is_move_constructible<T>::value && std::is_move_assignable<T>::value
504 #else
505                                       && std::is_nothrow_move_constructible<T>::value
506                                       && std::is_nothrow_move_assignable<T>::value
507 #endif
508                                       ));
509 };
510 
511 template <typename T>
512 const bool is_container_element<T>::value;
513 
514 inline namespace impl
515 {
516 
517 // Detection of ostreamable types.
518 template <typename T>
519 using ostreamable_t = decltype(std::declval<std::ostream &>() << std::declval<const T &>());
520 }
521 
522 /// Type trait for classes that can be output-streamed.
523 /**
524  * This type trait will be \p true if instances of type \p T can be directed to
525  * instances of \p std::ostream via the insertion operator. The operator must have a signature
526  * compatible with
527  * @code
528  * std::ostream &operator<<(std::ostream &, const T &)
529  * @endcode
530  */
531 template <typename T>
532 class is_ostreamable
533 {
534     static const bool implementation_defined = std::is_same<detected_t<ostreamable_t, T>, std::ostream &>::value;
535 
536 public:
537     /// Value of the type trait.
538     static const bool value = implementation_defined;
539 };
540 
541 template <typename T>
542 const bool is_ostreamable<T>::value;
543 
544 namespace detail
545 {
546 
547 template <typename T>
548 class is_hashable_impl : detail::sfinae_types
549 {
550     typedef typename std::decay<T>::type Td;
551     template <typename T1>
552     static auto test(const T1 &t) -> decltype(std::declval<const std::hash<T1> &>()(t));
553     static no test(...);
554 
555 public:
556     static const bool value = std::is_same<decltype(test(std::declval<Td>())), std::size_t>::value;
557 };
558 }
559 
560 /// Hashable type trait.
561 /**
562  * This type trait will be \p true if the decay type of \p T is hashable, \p false otherwise.
563  *
564  * A type \p T is hashable when supplied with a specialisation of \p std::hash which:
565  * - has a call operator complying to the interface specified by the C++ standard,
566  * - satisfies piranha::is_container_element.
567  *
568  * Note that depending on the implementation of the default \p std::hash class, using this type trait with
569  * a type which does not provide a specialisation for \p std::hash could result in a compilation error
570  * (e.g., if the unspecialised \p std::hash includes a \p false \p static_assert).
571  */
572 // NOTE: when we remove the is_container_element check we might need to make sure that std::hash is def ctible,
573 // depending on how we use it. Check.
574 template <typename T, typename = void>
575 class is_hashable
576 {
577 public:
578     /// Value of the type trait.
579     static const bool value = false;
580 };
581 
582 template <typename T>
583 class is_hashable<T, typename std::enable_if<detail::is_hashable_impl<T>::value>::type>
584 {
585     typedef typename std::decay<T>::type Td;
586     typedef std::hash<Td> hasher;
587 
588 public:
589     static const bool value = is_container_element<hasher>::value;
590 };
591 
592 template <typename T, typename Enable>
593 const bool is_hashable<T, Enable>::value;
594 
595 template <typename T>
596 const bool is_hashable<T, typename std::enable_if<detail::is_hashable_impl<T>::value>::type>::value;
597 
598 namespace detail
599 {
600 
601 template <typename, typename, typename = void>
602 struct is_function_object_impl {
603     template <typename... Args>
604     struct tt {
605         static const bool value = false;
606     };
607 };
608 
609 template <typename T, typename ReturnType>
610 struct is_function_object_impl<T, ReturnType, typename std::enable_if<std::is_class<T>::value>::type> {
611     template <typename... Args>
612     struct tt : detail::sfinae_types {
613         template <typename U>
614         static auto test(U &f) -> decltype(f(std::declval<Args>()...));
615         static no test(...);
616         static const bool value = std::is_same<decltype(test(*(T *)nullptr)), ReturnType>::value;
617     };
618 };
619 }
620 
621 /// Function object type trait.
622 /**
623  * This type trait will be true if \p T is a function object returning \p ReturnType and taking
624  * \p Args as arguments. That is, the type trait will be \p true if the following conditions are met:
625  * - \p T is a class,
626  * - \p T is equipped with a call operator returning \p ReturnType and taking \p Args as arguments.
627  *
628  * \p T can be const qualified (in which case the call operator must be also const in order for the type trait to be
629  * satisfied).
630  */
631 template <typename T, typename ReturnType, typename... Args>
632 class is_function_object
633 {
634 public:
635     /// Value of the type trait.
636     static const bool value = detail::is_function_object_impl<T, ReturnType>::template tt<Args...>::value;
637 };
638 
639 template <typename T, typename ReturnType, typename... Args>
640 const bool is_function_object<T, ReturnType, Args...>::value;
641 
642 /// Type trait to detect hash function objects.
643 /**
644  * \p T is a hash function object for \p U if the following requirements are met:
645  * - \p T is a function object with const call operator accepting as input const \p U and returning \p std::size_t,
646  * - \p T satisfies piranha::is_container_element.
647  *
648  * The decay type of \p U is considered in this type trait.
649  */
650 template <typename T, typename U, typename = void>
651 class is_hash_function_object
652 {
653 public:
654     /// Value of the type trait.
655     static const bool value = false;
656 };
657 
658 template <typename T, typename U>
659 class is_hash_function_object<T, U, typename std::enable_if<is_function_object<
660                                         typename std::add_const<T>::type, std::size_t,
661                                         typename std::decay<U>::type const &>::value>::type>
662 {
663     typedef typename std::decay<U>::type Ud;
664 
665 public:
666     static const bool value = is_container_element<T>::value;
667 };
668 
669 template <typename T, typename U, typename Enable>
670 const bool is_hash_function_object<T, U, Enable>::value;
671 
672 template <typename T, typename U>
673 const bool is_hash_function_object<T, U, typename std::enable_if<is_function_object<
674                                              typename std::add_const<T>::type, std::size_t,
675                                              typename std::decay<U>::type const &>::value>::type>::value;
676 
677 /// Type trait to detect equality function objects.
678 /**
679  * \p T is an equality function object for \p U if the following requirements are met:
680  * - \p T is a function object with const call operator accepting as input two const \p U and returning \p bool,
681  * - \p T satisfies piranha::is_container_element.
682  *
683  * The decay type of \p U is considered in this type trait.
684  */
685 template <typename T, typename U, typename = void>
686 class is_equality_function_object
687 {
688 public:
689     /// Value of the type trait.
690     static const bool value = false;
691 };
692 
693 template <typename T, typename U>
694 class is_equality_function_object<T, U,
695                                   typename std::enable_if<is_function_object<
696                                       typename std::add_const<T>::type, bool, typename std::decay<U>::type const &,
697                                       typename std::decay<U>::type const &>::value>::type>
698 {
699 public:
700     static const bool value = is_container_element<T>::value;
701 };
702 
703 template <typename T, typename U, typename Enable>
704 const bool is_equality_function_object<T, U, Enable>::value;
705 
706 template <typename T, typename U>
707 const bool
708     is_equality_function_object<T, U, typename std::enable_if<is_function_object<
709                                           typename std::add_const<T>::type, bool, typename std::decay<U>::type const &,
710                                           typename std::decay<U>::type const &>::value>::type>::value;
711 }
712 
713 /// Macro to test if class has type definition.
714 /**
715  * This macro will declare a template struct parametrized over one type \p T and called <tt>has_typedef_type_name</tt>,
716  * whose static const bool member \p value will be \p true if \p T contains a \p typedef called \p type_name, false
717  * otherwise.
718  *
719  * For instance:
720  * \code
721  * PIRANHA_DECLARE_HAS_TYPEDEF(foo_type);
722  * struct foo
723  * {
724  * 	typedef int foo_type;
725  * };
726  * struct bar {};
727  * \endcode
728  * \p has_typedef_foo_type<foo>::value will be true and \p has_typedef_foo_type<bar>::value will be false.
729  *
730  * The decay type of the template argument is considered by the class defined by the macro.
731  */
732 #define PIRANHA_DECLARE_HAS_TYPEDEF(type_name)                                                                         \
733     template <typename PIRANHA_DECLARE_HAS_TYPEDEF_ARGUMENT>                                                           \
734     class has_typedef_##type_name : piranha::detail::sfinae_types                                                      \
735     {                                                                                                                  \
736         using Td_ = typename std::decay<PIRANHA_DECLARE_HAS_TYPEDEF_ARGUMENT>::type;                                   \
737         template <typename T_>                                                                                         \
738         static auto test(const T_ *) -> decltype(std::declval<typename T_::type_name>(), void(), yes());               \
739         static no test(...);                                                                                           \
740                                                                                                                        \
741     public:                                                                                                            \
742         static const bool value = std::is_same<yes, decltype(test((Td_ *)nullptr))>::value;                            \
743     }
744 
745 /// Macro for static type trait checks.
746 /**
747  * This macro will check via a \p static_assert that the template type trait \p tt provides a \p true \p value.
748  * The variadic arguments are interpreted as the template arguments of \p tt.
749  */
750 #define PIRANHA_TT_CHECK(tt, ...)                                                                                      \
751     static_assert(tt<__VA_ARGS__>::value, "type trait check failure -> " #tt "<" #__VA_ARGS__ ">")
752 
753 namespace piranha
754 {
755 
756 namespace detail
757 {
758 
759 template <typename T, typename... Args>
760 struct min_int_impl {
761     using next = typename min_int_impl<Args...>::type;
762     static_assert((std::is_unsigned<T>::value && std::is_unsigned<next>::value && std::is_integral<next>::value)
763                       || (std::is_signed<T>::value && std::is_signed<next>::value && std::is_integral<next>::value),
764                   "The type trait's arguments must all be (un)signed integers.");
765     using type = typename std::conditional<(std::numeric_limits<T>::max() < std::numeric_limits<next>::max()
766                                             && (std::is_unsigned<T>::value
767                                                 || std::numeric_limits<T>::min() > std::numeric_limits<next>::min())),
768                                            T, next>::type;
769 };
770 
771 template <typename T>
772 struct min_int_impl<T> {
773     static_assert(std::is_integral<T>::value, "The type trait's arguments must all be (un)signed integers.");
774     using type = T;
775 };
776 
777 template <typename T, typename... Args>
778 struct max_int_impl {
779     using next = typename max_int_impl<Args...>::type;
780     static_assert((std::is_unsigned<T>::value && std::is_unsigned<next>::value && std::is_integral<next>::value)
781                       || (std::is_signed<T>::value && std::is_signed<next>::value && std::is_integral<next>::value),
782                   "The type trait's arguments must all be (un)signed integers.");
783     using type = typename std::conditional<(std::numeric_limits<T>::max() > std::numeric_limits<next>::max()
784                                             && (std::is_unsigned<T>::value
785                                                 || std::numeric_limits<T>::min() < std::numeric_limits<next>::min())),
786                                            T, next>::type;
787 };
788 
789 template <typename T>
790 struct max_int_impl<T> {
791     static_assert(std::is_integral<T>::value, "The type trait's arguments must all be (un)signed integers.");
792     using type = T;
793 };
794 }
795 
796 /// Detect narrowest integer type
797 /**
798  * This type alias requires \p T and \p Args (if any) to be all signed or unsigned integer types.
799  * It will be defined as the input type with the narrowest numerical range.
800  */
801 template <typename T, typename... Args>
802 using min_int = typename detail::min_int_impl<T, Args...>::type;
803 
804 /// Detect widest integer type
805 /**
806  * This type alias requires \p T and \p Args (if any) to be all signed or unsigned integer types.
807  * It will be defined as the input type with the widest numerical range.
808  */
809 template <typename T, typename... Args>
810 using max_int = typename detail::max_int_impl<T, Args...>::type;
811 
812 namespace detail
813 {
814 
815 #if !defined(PIRANHA_DOXYGEN_INVOKED)
816 
817 // Detect the availability of std::iterator_traits on type It, plus a couple more requisites from the
818 // iterator concept.
819 // NOTE: this needs also the is_swappable type trait, but this seems to be difficult to implement in C++11. Mostly
820 // because
821 // it seems that:
822 // - we cannot detect a specialised std::swap (so if it is not specialised, it will pick the default implementation
823 //   which could fail in the implementation without giving hints in the prototype),
824 // - it's tricky to fulfill the requirement that swap has to be called unqualified (cannot use 'using std::swap' within
825 // a decltype()
826 //   SFINAE, might be doable with automatic return type deduction for regular functions in C++14?).
827 template <typename It>
828 struct has_iterator_traits {
829     using it_tags = std::tuple<std::input_iterator_tag, std::output_iterator_tag, std::forward_iterator_tag,
830                                std::bidirectional_iterator_tag, std::random_access_iterator_tag>;
831     PIRANHA_DECLARE_HAS_TYPEDEF(difference_type);
832     PIRANHA_DECLARE_HAS_TYPEDEF(value_type);
833     PIRANHA_DECLARE_HAS_TYPEDEF(pointer);
834     PIRANHA_DECLARE_HAS_TYPEDEF(reference);
835     PIRANHA_DECLARE_HAS_TYPEDEF(iterator_category);
836     using i_traits = std::iterator_traits<It>;
837     static const bool value = has_typedef_reference<i_traits>::value && has_typedef_value_type<i_traits>::value
838                               && has_typedef_pointer<i_traits>::value && has_typedef_difference_type<i_traits>::value
839                               && has_typedef_iterator_category<i_traits>::value && std::is_copy_constructible<It>::value
840                               && std::is_copy_assignable<It>::value && std::is_destructible<It>::value;
841 };
842 
843 template <typename It>
844 const bool has_iterator_traits<It>::value;
845 
846 // TMP to check if a type is convertible to a type in the tuple.
847 template <typename T, typename Tuple, std::size_t I = 0u, typename Enable = void>
848 struct convertible_type_in_tuple {
849     static_assert(I < std::numeric_limits<std::size_t>::max(), "Overflow error.");
850     static const bool value = std::is_convertible<T, typename std::tuple_element<I, Tuple>::type>::value
851                               || convertible_type_in_tuple<T, Tuple, I + 1u>::value;
852 };
853 
854 template <typename T, typename Tuple, std::size_t I>
855 struct convertible_type_in_tuple<T, Tuple, I, typename std::enable_if<I == std::tuple_size<Tuple>::value>::type> {
856     static const bool value = false;
857 };
858 
859 template <typename T, typename = void>
860 struct is_iterator_impl {
861     static const bool value = false;
862 };
863 
864 // NOTE: here the correct condition is the commented one, as opposed to the first one appearing. However, it seems like
865 // there are inconsistencies between the commented condition and the definition of many output iterators in the standard
866 // library:
867 //
868 // http://stackoverflow.com/questions/23567244/apparent-inconsistency-in-iterator-requirements
869 //
870 // Until this is clarified, it is probably better to keep this workaround.
871 template <typename T>
872 struct is_iterator_impl<T,
873                         typename std::
874                             enable_if</*std::is_same<typename
875                                          std::iterator_traits<T>::reference,decltype(*std::declval<T &>())>::value &&*/
876                                       // That is the one that would need to be replaced with the one above. Just check
877                                       // that operator*() is defined.
878                                       std::is_same<decltype(*std::declval<T &>()),
879                                                    decltype(*std::declval<T &>())>::value
880                                       && std::is_same<decltype(++std::declval<T &>()), T &>::value
881                                       && has_iterator_traits<T>::value &&
882                                       // NOTE: here we used to have type_in_tuple, but it turns out Boost.iterator
883                                       // defines its own set of tags derived from the standard
884                                       // ones. Hence, check that the category can be converted to one of the standard
885                                       // categories. This should not change anything for std iterators,
886                                       // and just enable support for Boost ones.
887                                       convertible_type_in_tuple<typename std::iterator_traits<T>::iterator_category,
888                                                                 typename has_iterator_traits<T>::it_tags>::value>::
889                                 type> {
890     static const bool value = true;
891 };
892 
893 #endif
894 }
895 
896 /// Iterator type trait.
897 /**
898  * This type trait will be \p true if the decay type of \p T satisfies the compile-time requirements of an iterator (as
899  * defined by the C++ standard),
900  * \p false otherwise.
901  */
902 template <typename T>
903 struct is_iterator {
904     /// Value of the type trait.
905     static const bool value = detail::is_iterator_impl<typename std::decay<T>::type>::value;
906 };
907 
908 template <typename T>
909 const bool is_iterator<T>::value;
910 
911 namespace detail
912 {
913 
914 #if !defined(PIRANHA_DOXYGEN_INVOKED)
915 
916 template <typename T, typename = void>
917 struct is_input_iterator_impl {
918     static const bool value = false;
919 };
920 
921 template <typename T, typename = void>
922 struct arrow_operator_type {
923 };
924 
925 template <typename T>
926 struct arrow_operator_type<T, typename std::enable_if<std::is_pointer<T>::value>::type> {
927     using type = T;
928 };
929 
930 template <typename T>
931 struct arrow_operator_type<T, typename std::enable_if<std::is_same<
932                                   typename arrow_operator_type<decltype(std::declval<T &>().operator->())>::type,
933                                   typename arrow_operator_type<decltype(std::declval<T &>().operator->())>::type>::
934                                                           value>::type> {
935     using type = typename arrow_operator_type<decltype(std::declval<T &>().operator->())>::type;
936 };
937 
938 template <typename T>
939 struct is_input_iterator_impl<T,
940                               typename std::
941                                   enable_if<is_iterator_impl<T>::value && is_equality_comparable<T>::value
942                                             && std::is_convertible<decltype(*std::declval<T &>()),
943                                                                    typename std::iterator_traits<T>::value_type>::value
944                                             && std::is_same<decltype(++std::declval<T &>()), T &>::value
945                                             && std::is_same<decltype((void)std::declval<T &>()++),
946                                                             decltype((void)std::declval<T &>()++)>::value
947                                             && std::is_convertible<decltype(*std::declval<T &>()++),
948                                                                    typename std::iterator_traits<T>::value_type>::value
949                                             &&
950                                             // NOTE: here we know that the arrow op has to return a pointer, if
951                                             // implemented correctly, and that the syntax
952                                             // it->m must be equivalent to (*it).m. This means that, barring differences
953                                             // in reference qualifications,
954                                             // it-> and *it must return the same thing.
955                                             std::is_same<
956                                                 typename std::remove_reference<decltype(
957                                                     *std::declval<typename arrow_operator_type<T>::type>())>::type,
958                                                 typename std::remove_reference<decltype(*std::declval<T &>())>::type>::
959                                                 value
960                                             &&
961                                             // NOTE: here the usage of is_convertible guarantees we catch both iterators
962                                             // higher in the type hierarchy and
963                                             // the Boost versions of standard iterators as well.
964                                             std::is_convertible<typename std::iterator_traits<T>::iterator_category,
965                                                                 std::input_iterator_tag>::value>::type> {
966     static const bool value = true;
967 };
968 
969 #endif
970 }
971 
972 /// Input iterator type trait.
973 /**
974  * This type trait will be \p true if the decay type of \p T satisfies the compile-time requirements of an input
975  * iterator (as defined by the C++ standard),
976  * \p false otherwise.
977  */
978 template <typename T>
979 struct is_input_iterator {
980     /// Value of the type trait.
981     static const bool value = detail::is_input_iterator_impl<typename std::decay<T>::type>::value;
982 };
983 
984 template <typename T>
985 const bool is_input_iterator<T>::value;
986 
987 namespace detail
988 {
989 
990 template <typename T, typename = void>
991 struct is_forward_iterator_impl {
992     static const bool value = false;
993 };
994 
995 template <typename T>
996 struct is_forward_iterator_impl<T,
997                                 typename std::
998                                     enable_if<is_input_iterator_impl<T>::value
999                                               && std::is_default_constructible<T>::value
1000                                               && (std::is_same<typename std::iterator_traits<T>::value_type &,
1001                                                                typename std::iterator_traits<T>::reference>::value
1002                                                   || std::is_same<typename std::iterator_traits<T>::value_type const &,
1003                                                                   typename std::iterator_traits<T>::reference>::value)
1004                                               && std::is_convertible<decltype(std::declval<T &>()++), const T &>::value
1005                                               && std::is_same<decltype(*std::declval<T &>()++),
1006                                                               typename std::iterator_traits<T>::reference>::value
1007                                               && std::is_convertible<
1008                                                      typename std::iterator_traits<T>::iterator_category,
1009                                                      std::forward_iterator_tag>::value>::type> {
1010     static const bool value = true;
1011 };
1012 }
1013 
1014 /// Forward iterator type trait.
1015 /**
1016  * This type trait will be \p true if the decay type of \p T satisfies the compile-time requirements of a forward
1017  * iterator (as defined by the C++ standard),
1018  * \p false otherwise.
1019  */
1020 template <typename T>
1021 struct is_forward_iterator {
1022     /// Value of the type trait.
1023     static const bool value = detail::is_forward_iterator_impl<typename std::decay<T>::type>::value;
1024 };
1025 
1026 template <typename T>
1027 const bool is_forward_iterator<T>::value;
1028 
1029 namespace detail
1030 {
1031 
1032 #if !defined(PIRANHA_DOXYGEN_INVOKED)
1033 template <typename T>
safe_abs_sint_impl(T cur_p=T (1),T cur_n=T (-1))1034 constexpr T safe_abs_sint_impl(T cur_p = T(1), T cur_n = T(-1))
1035 {
1036     return (cur_p > std::numeric_limits<T>::max() / T(2) || cur_n < std::numeric_limits<T>::min() / T(2))
1037                ? cur_p
1038                : safe_abs_sint_impl(static_cast<T>(cur_p * 2), static_cast<T>(cur_n * 2));
1039 }
1040 
1041 // Determine, for the signed integer T, a value n, power of 2, such that it is safe to take -n.
1042 template <typename T>
1043 struct safe_abs_sint {
1044     static_assert(std::is_integral<T>::value && std::is_signed<T>::value, "T must be a signed integral type.");
1045     static const T value = safe_abs_sint_impl<T>();
1046 };
1047 
1048 template <typename T>
1049 const T safe_abs_sint<T>::value;
1050 #endif
1051 
1052 // A simple true type-trait that can be used inside enable_if with T a decltype() expression
1053 // subject to SFINAE. It is similar to the proposed void_t type (in C++14, maybe?).
1054 template <typename T>
1055 struct true_tt {
1056     static const bool value = true;
1057 };
1058 
1059 template <typename T>
1060 const bool true_tt<T>::value;
1061 }
1062 
1063 /// Detect the availability of <tt>std::begin()</tt> and <tt>std::end()</tt>.
1064 /**
1065  * This type trait will be \p true if all the following conditions are fulfilled:
1066  *
1067  * - <tt>std::begin()</tt> and <tt>std::end()</tt> can be called on instances of \p T, yielding the type \p It,
1068  * - \p It is an input iterator.
1069  *
1070  * Any reference qualification in \p T is ignored by this type trait.
1071  */
1072 template <typename T>
1073 class has_begin_end : detail::sfinae_types
1074 {
1075     using Td = typename std::remove_reference<T>::type;
1076     template <typename T1>
1077     static auto test1(T1 &t) -> decltype(std::begin(t));
1078     static no test1(...);
1079     template <typename T1>
1080     static auto test2(T1 &t) -> decltype(std::end(t));
1081     static no test2(...);
1082 
1083 public:
1084     /// Value of the type trait.
1085     static const bool value
1086         = is_input_iterator<decltype(test1(std::declval<Td &>()))>::value
1087           && is_input_iterator<decltype(test2(std::declval<Td &>()))>::value
1088           && std::is_same<decltype(test1(std::declval<Td &>())), decltype(test2(std::declval<Td &>()))>::value;
1089 };
1090 
1091 template <typename T>
1092 const bool has_begin_end<T>::value;
1093 
1094 /// Left-shift type trait.
1095 /**
1096  * Will be \p true if objects of type \p T can be left-shifted by objects of type \p U.
1097  *
1098  * This type trait will strip \p T and \p U of reference qualifiers, and it will test the operator in the form
1099  @code
1100  operator<<(const Td &, const Ud &)
1101  @endcode
1102  * where \p Td and \p Ud are \p T and \p U after the removal of reference qualifiers. E.g.:
1103  @code
1104  has_left_shift<int>::value == true;
1105  has_left_shift<int,std::string>::value == false;
1106  @endcode
1107  */
1108 template <typename T, typename U = T>
1109 class has_left_shift : detail::sfinae_types
1110 {
1111     friend struct arith_tt_helper<T, U, has_left_shift<T, U>>;
1112     template <typename T1, typename U1>
1113     static auto test(const T1 &t, const U1 &u) -> decltype(t << u, void(), yes());
1114     static no test(...);
1115 
1116 public:
1117     /// Value of the type trait.
1118     static const bool value = arith_tt_helper<T, U, has_left_shift>::value;
1119 };
1120 
1121 template <typename T, typename U>
1122 const bool has_left_shift<T, U>::value;
1123 
1124 /// Right-shift type trait.
1125 /**
1126  * Will be \p true if objects of type \p T can be right-shifted by objects of type \p U.
1127  *
1128  * This type trait will strip \p T and \p U of reference qualifiers, and it will test the operator in the form
1129  @code
1130  operator>>(const Td &, const Ud &)
1131  @endcode
1132  * where \p Td and \p Ud are \p T and \p U after the removal of reference qualifiers. E.g.:
1133  @code
1134  has_right_shift<int>::value == true;
1135  has_right_shift<int,std::string>::value == false;
1136  @endcode
1137  */
1138 template <typename T, typename U = T>
1139 class has_right_shift : detail::sfinae_types
1140 {
1141     friend struct arith_tt_helper<T, U, has_right_shift<T, U>>;
1142     template <typename T1, typename U1>
1143     static auto test(const T1 &t, const U1 &u) -> decltype(t >> u, void(), yes());
1144     static no test(...);
1145 
1146 public:
1147     /// Value of the type trait.
1148     static const bool value = arith_tt_helper<T, U, has_right_shift>::value;
1149 };
1150 
1151 template <typename T, typename U>
1152 const bool has_right_shift<T, U>::value;
1153 
1154 /// In-place left-shift type trait.
1155 /**
1156  * Will be \p true if objects of type \p T can be left-shifted in-place by objects of type \p U.
1157  *
1158  * This type trait will strip \p T and \p U of reference qualifiers, and it will test the operator in the form
1159  @code
1160  operator<<=(Td &, const Ud &)
1161  @endcode
1162  * where \p Td and \p Ud are \p T and \p U after the removal of reference qualifiers. E.g.:
1163  @code
1164  has_left_shift_in_place<int>::value == true;
1165  has_left_shift_in_place<int,std::string>::value == false;
1166  @endcode
1167  */
1168 template <typename T, typename U = T>
1169 class has_left_shift_in_place : detail::sfinae_types
1170 {
1171     friend struct arith_tt_helper<T, U, has_left_shift_in_place<T, U>>;
1172     template <typename T1, typename U1>
1173     static auto test(T1 &t, const U1 &u) -> decltype(t <<= u, void(), yes());
1174     static no test(...);
1175 
1176 public:
1177     /// Value of the type trait.
1178     static const bool value = arith_tt_helper<T, U, has_left_shift_in_place>::value;
1179 };
1180 
1181 template <typename T, typename U>
1182 const bool has_left_shift_in_place<T, U>::value;
1183 
1184 /// In-place right-shift type trait.
1185 /**
1186  * Will be \p true if objects of type \p T can be right-shifted in-place by objects of type \p U.
1187  *
1188  * This type trait will strip \p T and \p U of reference qualifiers, and it will test the operator in the form
1189  @code
1190  operator<<=(Td &, const Ud &)
1191  @endcode
1192  * where \p Td and \p Ud are \p T and \p U after the removal of reference qualifiers. E.g.:
1193  @code
1194  has_right_shift_in_place<int>::value == true;
1195  has_right_shift_in_place<int,std::string>::value == false;
1196  @endcode
1197  */
1198 template <typename T, typename U = T>
1199 class has_right_shift_in_place : detail::sfinae_types
1200 {
1201     friend struct arith_tt_helper<T, U, has_right_shift_in_place<T, U>>;
1202     template <typename T1, typename U1>
1203     static auto test(T1 &t, const U1 &u) -> decltype(t >>= u, void(), yes());
1204     static no test(...);
1205 
1206 public:
1207     /// Value of the type trait.
1208     static const bool value = arith_tt_helper<T, U, has_right_shift_in_place>::value;
1209 };
1210 
1211 template <typename T, typename U>
1212 const bool has_right_shift_in_place<T, U>::value;
1213 
1214 /// Detect if type has exact ring operations.
1215 /**
1216  * This type trait should be specialised to \p true if the decay type of \p T supports exact
1217  * addition, subtraction and multiplication.
1218  */
1219 template <typename T, typename = void>
1220 struct has_exact_ring_operations {
1221     /// Value of the type trait.
1222     /**
1223      * The default implementation will set the value to \p false.
1224      */
1225     static const bool value = false;
1226 };
1227 
1228 template <typename T, typename Enable>
1229 const bool has_exact_ring_operations<T, Enable>::value;
1230 
1231 /// Detect if type can be returned from a function.
1232 template <typename T>
1233 struct is_returnable {
1234 private:
1235     static const bool implementation_defined
1236         = disjunction<std::is_same<T, void>,
1237                       conjunction<std::is_destructible<T>,
1238                                   disjunction<std::is_copy_constructible<T>, std::is_move_constructible<T>>>>::value;
1239 
1240 public:
1241     /// Value of the type trait.
1242     /**
1243      * The type trait will be true if \p T is destructible and copy or move
1244      * constructible, or if \p T is \p void.
1245      */
1246     static const bool value = implementation_defined;
1247 };
1248 
1249 template <typename T>
1250 const bool is_returnable<T>::value;
1251 
1252 /// Detect types that can be used as mapped values in associative containers.
1253 /**
1254  * This type trait is intended to detect whether \p T supports typical operations that can be performed
1255  * on the mapped values in associative containers.
1256  * Specifically, this trait will be \p true if all the following conditions hold:
1257  * - \p T is default constructible,
1258  * - \p T is copy constructible and assignable,
1259  * - \p T is move constructible and assignable,
1260  * - \p T is destructible.
1261  *
1262  * Otherwise, the value of this trait will be \p false.
1263  */
1264 template <typename T>
1265 struct is_mappable {
1266 private:
1267     static const bool implementation_defined
1268         = std::is_default_constructible<T>::value && std::is_destructible<T>::value
1269           && std::is_copy_constructible<T>::value && std::is_copy_assignable<T>::value
1270           && std::is_move_constructible<T>::value && std::is_move_assignable<T>::value;
1271 
1272 public:
1273     /// Value of the type trait.
1274     static const bool value = implementation_defined;
1275 };
1276 
1277 template <typename T>
1278 const bool is_mappable<T>::value;
1279 
1280 /// Detect if zero is a multiplicative absorber.
1281 /**
1282  * This type trait, defaulting to \p true, establishes if the zero element of the type \p T is a multiplicative
1283  * absorber. That is, if for any value \f$ x \f$ of type \p T the relation
1284  * \f[
1285  * x \times 0 = 0
1286  * \f]
1287  * holds, then this type trait should be \p true.
1288  *
1289  * This type trait requires \p T to satsify piranha::is_multipliable, after removal of cv/reference qualifiers.
1290  * This type trait can be specialised via \p std::enable_if.
1291  */
1292 template <typename T, typename = void>
1293 struct zero_is_absorbing {
1294 private:
1295     PIRANHA_TT_CHECK(is_multipliable, uncvref_t<T>);
1296 
1297 public:
1298     /// Value of the type trait.
1299     static const bool value = true;
1300 };
1301 
1302 template <typename T, typename Enable>
1303 const bool zero_is_absorbing<T, Enable>::value;
1304 
1305 inline namespace impl
1306 {
1307 
1308 template <typename T>
1309 using fp_zero_is_absorbing_enabler = enable_if_t<std::is_floating_point<uncvref_t<T>>::value
1310                                                  && (std::numeric_limits<uncvref_t<T>>::has_quiet_NaN
1311                                                      || std::numeric_limits<uncvref_t<T>>::has_signaling_NaN)>;
1312 }
1313 
1314 /// Specialisation of piranha::zero_is_absorbing for floating-point types.
1315 /**
1316  * \note
1317  * This specialisation is enabled if \p T, after the removal of cv/reference qualifiers, is a C++ floating-point type
1318  * supporting NaN.
1319  *
1320  * In the presence of NaN, a floating-point zero is not the multiplicative absorbing element.
1321  *
1322  * This type traits requires \p T to satsify piranha::is_multipliable, after the removal of cv/reference qualifiers.
1323  */
1324 template <typename T>
1325 struct zero_is_absorbing<T, fp_zero_is_absorbing_enabler<T>> {
1326 private:
1327     PIRANHA_TT_CHECK(is_multipliable, uncvref_t<T>);
1328 
1329 public:
1330     /// Value of the type trait.
1331     static const bool value = false;
1332 };
1333 
1334 template <typename T>
1335 const bool zero_is_absorbing<T, fp_zero_is_absorbing_enabler<T>>::value;
1336 }
1337 
1338 #endif
1339