|
9 | 9 | // type_traits
|
10 | 10 |
|
11 | 11 | // underlying_type
|
| 12 | +// As of C++20, std::underlying_type is SFINAE-friendly; if you hand it |
| 13 | +// a non-enumeration, it returns an empty struct. |
12 | 14 |
|
13 | 15 | #include <type_traits>
|
14 | 16 | #include <climits>
|
15 | 17 |
|
16 | 18 | #include "test_macros.h"
|
17 | 19 |
|
18 |
| -enum E { V = INT_MIN }; |
19 | 20 |
|
| 21 | +// MSVC's ABI doesn't follow the standard |
20 | 22 | #if !defined(_WIN32) || defined(__MINGW32__)
|
21 | 23 | #define TEST_UNSIGNED_UNDERLYING_TYPE 1
|
22 |
| -#else |
23 |
| - #define TEST_UNSIGNED_UNDERLYING_TYPE 0 // MSVC's ABI doesn't follow the Standard |
24 | 24 | #endif
|
25 | 25 |
|
26 |
| -#if TEST_UNSIGNED_UNDERLYING_TYPE |
| 26 | + |
| 27 | +#if TEST_STD_VER > 17 |
| 28 | +template <class, class = std::void_t<>> |
| 29 | +struct has_type_member : std::false_type {}; |
| 30 | + |
| 31 | +template <class T> |
| 32 | +struct has_type_member<T, |
| 33 | + std::void_t<typename std::underlying_type<T>::type>> : std::true_type {}; |
| 34 | + |
| 35 | +struct S {}; |
| 36 | +union U { int i; float f;}; |
| 37 | +#endif |
| 38 | + |
| 39 | +template <typename T, typename Expected> |
| 40 | +void check() |
| 41 | +{ |
| 42 | + ASSERT_SAME_TYPE(Expected, typename std::underlying_type<T>::type); |
| 43 | +#if TEST_STD_VER > 11 |
| 44 | + ASSERT_SAME_TYPE(Expected, typename std::underlying_type_t<T>); |
| 45 | +#endif |
| 46 | +} |
| 47 | + |
| 48 | +enum E { V = INT_MIN }; |
| 49 | + |
| 50 | +#ifdef TEST_UNSIGNED_UNDERLYING_TYPE |
27 | 51 | enum F { W = UINT_MAX };
|
28 | 52 | #endif // TEST_UNSIGNED_UNDERLYING_TYPE
|
29 | 53 |
|
| 54 | +#if TEST_STD_VER >= 11 |
| 55 | +enum G : char {}; |
| 56 | +enum class H { red, green = 20, blue }; |
| 57 | +enum class I : long { red, green = 20, blue }; |
| 58 | +enum struct J { red, green = 20, blue }; |
| 59 | +enum struct K : short { red, green = 20, blue }; |
| 60 | +#endif |
| 61 | + |
30 | 62 | int main(int, char**)
|
31 | 63 | {
|
32 |
| - ASSERT_SAME_TYPE(int, std::underlying_type<E>::type); |
33 |
| -#if TEST_UNSIGNED_UNDERLYING_TYPE |
34 |
| - ASSERT_SAME_TYPE(unsigned, std::underlying_type<F>::type); |
| 64 | +// Basic tests |
| 65 | + check<E, int>(); |
| 66 | +#ifdef TEST_UNSIGNED_UNDERLYING_TYPE |
| 67 | + check<F, unsigned>(); |
35 | 68 | #endif // TEST_UNSIGNED_UNDERLYING_TYPE
|
36 | 69 |
|
37 |
| -#if TEST_STD_VER > 11 |
38 |
| - ASSERT_SAME_TYPE(int, std::underlying_type_t<E>); |
39 |
| -#if TEST_UNSIGNED_UNDERLYING_TYPE |
40 |
| - ASSERT_SAME_TYPE(unsigned, std::underlying_type_t<F>); |
41 |
| -#endif // TEST_UNSIGNED_UNDERLYING_TYPE |
42 |
| -#endif // TEST_STD_VER > 11 |
43 |
| - |
| 70 | +// Class enums and enums with specified underlying type |
44 | 71 | #if TEST_STD_VER >= 11
|
45 |
| - enum G : char { }; |
| 72 | + check<G, char>(); |
| 73 | + check<H, int>(); |
| 74 | + check<I, long>(); |
| 75 | + check<J, int>(); |
| 76 | + check<K, short>(); |
| 77 | +#endif |
46 | 78 |
|
47 |
| - ASSERT_SAME_TYPE(char, std::underlying_type<G>::type); |
48 |
| -#if TEST_STD_VER > 11 |
49 |
| - ASSERT_SAME_TYPE(char, std::underlying_type_t<G>); |
50 |
| -#endif // TEST_STD_VER > 11 |
51 |
| -#endif // TEST_STD_VER >= 11 |
| 79 | +// SFINAE-able underlying_type |
| 80 | +#if TEST_STD_VER > 17 |
| 81 | + static_assert( has_type_member<E>::value, ""); |
| 82 | + static_assert( has_type_member<F>::value, ""); |
| 83 | + static_assert( has_type_member<G>::value, ""); |
| 84 | + |
| 85 | + static_assert(!has_type_member<void>::value, ""); |
| 86 | + static_assert(!has_type_member<int>::value, ""); |
| 87 | + static_assert(!has_type_member<double>::value, ""); |
| 88 | + static_assert(!has_type_member<int[]>::value, ""); |
| 89 | + static_assert(!has_type_member<S>::value, ""); |
| 90 | + static_assert(!has_type_member<void (S::*)(int)>::value, ""); |
| 91 | + static_assert(!has_type_member<void (S::*)(int, ...)>::value, ""); |
| 92 | + static_assert(!has_type_member<U>::value, ""); |
| 93 | + static_assert(!has_type_member<void(int)>::value, ""); |
| 94 | + static_assert(!has_type_member<void(int, ...)>::value, ""); |
| 95 | + static_assert(!has_type_member<int&>::value, ""); |
| 96 | + static_assert(!has_type_member<int&&>::value, ""); |
| 97 | + static_assert(!has_type_member<int*>::value, ""); |
| 98 | + static_assert(!has_type_member<std::nullptr_t>::value, ""); |
| 99 | +#endif |
52 | 100 |
|
53 | 101 | return 0;
|
54 | 102 | }
|
0 commit comments