-
Notifications
You must be signed in to change notification settings - Fork 1.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
<type_traits>
: Implement is_scoped_enum
#1950
Changes from 5 commits
36b02c6
696c536
62c430f
a18b27e
12f939a
834c677
f95529a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -366,6 +366,15 @@ struct is_enum : bool_constant<__is_enum(_Ty)> {}; // determine whether _Ty is a | |||||
template <class _Ty> | ||||||
_INLINE_VAR constexpr bool is_enum_v = __is_enum(_Ty); | ||||||
|
||||||
#if _HAS_CXX23 | ||||||
// STRUCT TEMPLATE is_scoped_enum | ||||||
template <class _Ty> | ||||||
inline constexpr bool is_scoped_enum_v = conjunction_v<is_enum<_Ty>, negation<is_convertible<_Ty, int>>>; | ||||||
|
||||||
template <class _Ty> | ||||||
struct is_scoped_enum : bool_constant<is_scoped_enum_v<_Ty>> {}; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am a bit torn, usually we always follow the convention to define the variable template and then derive the struct from it. In this case we require the short circuiting from
Suggested change
And then derive the variable template from that? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This was originally under Is there a general rule about this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I figure most people would be using the suffix version and doing it the traditional way would mean an extra template instantiation, wouldn't it? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Our modern convention is to follow the Standard order when it's easy to do so - however, the type traits are fairly jumbled, partly due to history, partly due to their dependencies. I wouldn't worry about the ordering. As for whether the struct or the variable template should be "fundamental" - it's the struct that's costly for throughput, whereas variable templates are cheap. The compiler has built-in optimizations for While it's possible to duplicate the implementation to improve the struct throughput slightly, as @miscco depicted, I don't think that's necessary here (because I don't think that this type trait will be used in a massive number of specializations - whereas the traits like |
||||||
#endif // _HAS_CXX23 | ||||||
|
||||||
// STRUCT TEMPLATE is_compound | ||||||
template <class _Ty> | ||||||
struct is_compound : bool_constant<!is_fundamental_v<_Ty>> {}; // determine whether _Ty is a compound type | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -76,6 +76,9 @@ STATIC_ASSERT(!is_unsigned_v<void>); | |
STATIC_ASSERT(!is_bounded_array_v<void>); | ||
STATIC_ASSERT(!is_unbounded_array_v<void>); | ||
#endif // _HAS_CXX20 | ||
#if _HAS_CXX23 | ||
STATIC_ASSERT(!is_scoped_enum_v<void>); | ||
#endif // _HAS_CXX23 | ||
|
||
STATIC_ASSERT(!is_constructible_v<void>); | ||
STATIC_ASSERT(!is_default_constructible_v<void>); | ||
|
@@ -629,6 +632,10 @@ void test_function_type() { | |
STATIC_ASSERT(!is_bounded_array_v<T>); | ||
STATIC_ASSERT(!is_unbounded_array_v<T>); | ||
#endif // _HAS_CXX20 | ||
|
||
#if _HAS_CXX23 | ||
STATIC_ASSERT(!is_scoped_enum_v<T>); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am missing some positive tests There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hence the draft PR and text body. Didn't know where the best place to put them in. |
||
#endif | ||
} | ||
|
||
template <typename T> | ||
|
@@ -851,6 +858,14 @@ enum LLEnum : long long { xLongExample, yLongExample }; | |
enum class ExampleEnumClass { xExample, yExample }; | ||
enum class LLEnumClass : long long { xLongExample, yLongExample }; | ||
|
||
#if _HAS_CXX23 | ||
STATIC_ASSERT(!is_scoped_enum<ExampleEnum>::value); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Should I remove this? Also I kinda forgot about the test on line 80, should I just create some enums up top (or move these enums?) and move this test block? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think it's good to have minimal coverage that the struct form works. The location of this test block seems fine to me - the previous locations in this file are doing different things. |
||
STATIC_ASSERT(!is_scoped_enum_v<ExampleEnum>); | ||
STATIC_ASSERT(!is_scoped_enum_v<test_abc1>); | ||
STATIC_ASSERT(is_scoped_enum_v<ExampleEnumClass>); | ||
STATIC_ASSERT(is_scoped_enum_v<LLEnumClass>); | ||
#endif // _HAS_CXX23 | ||
CaseyCarter marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
||
// P0258R2 has_unique_object_representations | ||
#if _HAS_CXX17 | ||
STATIC_ASSERT(!has_unique_object_representations_v<void>); | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's a
diabolicalbrilliantbrilliantly diabolical example. I think this means we need a compiler builtin.The Standardese is WG21-N4885 [dcl.enum]/6 "An enumeration whose underlying type is not fixed is an incomplete type until the closing
}
of its enum-specifier, at which point it becomes a complete type." and [meta.rqmts]/5 "Unless otherwise specified, an incomplete type may be used to instantiate a template specified in 20.15." There are no additional preconditions foris_scoped_enum
, so the incompleteenum E
is permitted.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
So I'm a little confused. Is that not supposed to compile?
This compiles just fine. If
is_scoped_enum_v
isn't supposed to work here then why doesis_enum_v
?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
IIUC
But I think it's valid to rely on the implementation-specific knowledge that
std::is_convertible_v
works in such case.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, that makes sense. Thanks.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That second example is quite the eyebrow raiser...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, so it sounds like (a)
is_convertible_v
is theoretically an issue here, but (b) in practice, MSVC's nonconformance allows this to compile and get the right answer. In any event, (c) this is an extreme corner case.I think we can proceed with the current implementation then. If we discover that the issue is worse than previously thought, we can always request a compiler builtin and replace the implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm inclined to request intrinsics now so the library can be prepared for MSVC's enumeration bugs to be fixed. When some compiler dev goes to audit what things break with the bug fixed, I'd rather we don't show up on that list.
In practice, this still looks like "go ahead and merge this implementation", the only difference would really be that we may update it to use intrinsics a bit sooner.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
FWIW, CWG-2516 has been accepted as a DR, so a scoped enum type is always complete when its name is declared, and hence no intrinsic is needed for
is_scoped_enum
. But we may still have to replace the current implementation once MSVC's bug (or non-conforming extension) is fixed.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed that MSVC has implemented
/Zc:enumTypes
(Enable enum type deduction), but it seems that despite this option, MSVC still believes that enumeration types are always complete, and that the underlying type isint
before the closing}
.