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