I couldn't view the the document (P0340R3). But I think this implementation should work.
Details
Diff Detail
Event Timeline
Here's the whole wording (from P0340):
template <class T> struct underlying_type;If T is an enumeration type, the member typedef type names the underlying type of T (9.6 [dcl.enum]); otherwise, there is no member type.
Mandates: T shall be a is not an incomplete enumeration type. (9.6)
So you'll need a test of an incomplete enumeration type.
include/type_traits | ||
---|---|---|
4702–4703 | I think I'd rather bury the boolean in an __impl class. | |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp | ||
66 | I prefer a failing test; so that we can look at the error message: no type named type in std::underlying_type<int> blah blah. |
include/type_traits | ||
---|---|---|
4702–4703 | +1. We shouldn't change the top-level declaration like this. It's technically an ABI break. | |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp | ||
65 | We're backporting this behavior and we can probably expect others to do so as well. Therefore the tests should run for all dialects we offer this behavior. | |
66 | Why not both? Passing tests are nice too. |
include/type_traits | ||
---|---|---|
4702–4703 | Will do. | |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp | ||
66 | The failing test error would be
There would be a note:
Is there any way to test for the note too? I will add both. |
include/type_traits | ||
---|---|---|
4713 | Nit. Space between underlying_type and :. | |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.fail.cpp | ||
23 | No need to get SFINAE involved here. Simply write: int main() { using T = std::underlying_type<int>::type; // expected-error {{boom}} } | |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp | ||
30 | I'm not sure what this test is actually testing. How about: template <class T, class = typename std::underlying_type<T>::type> std::true_type test_sfinae(int); template <class T> std::false_type test_sfinae(...); int main() { static_assert(decltype(test_sfinae<int>(0))::value == false, "not an enum"); static_assert(decltype(test_sfinae<E>(0))::value == true, "is an enum"); } |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp | ||
---|---|---|
30 | Now that we have the failing test, do we need this test at all? |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.fail.cpp | ||
---|---|---|
23 | Yes, your right. That's what happens when you copy and paste. | |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp | ||
30 | I like that idea @EricWF. Thinking more about how to structure these tests, I think the "fail" test is not super helpful (as is). What this should be testing for is whether passing underlying_type a non-enum type breaks the compiler. Given that it _shouldn't_ break the compiler, I think it is more helpful to make sure it passes than to try and make sure it fails in the "right" way. Let me know if you disagree though. |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp | ||
---|---|---|
30 | I don't know what you mean by "breaks the compiler" here. |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp | ||
---|---|---|
30 | I mean before this patch, the below code could not compile. The compiler would error when it tried to interpret the underlying_type template: template<class T> std::enable_if_t< std::is_same_v<std::underlying_type_t<T>, int> > test_sfinae(T t); void test_sfinae(int) { return; } int main() { test_sfinae(0); // Error while substituting deduced template arguments into function template 'test_sfinae' [with T = int] } |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp | ||
---|---|---|
30 | And you believe that the compiler was wrong to do so? |
You should probably have at least one (and probably several) simple tests that underlying_type<non enum type> is a valid type.
using Int = std::underlying_type<int>; using Float = std::underlying_type<float>;
etc.
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp | ||
---|---|---|
30 | Sorry, to clarify, what I meant by "breaks the compiler" was that there was an error while compiling. I did not mean that clang was broken in any way or did anything wrong. I should have chosen my wording more carefully. |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp | ||
---|---|---|
37 | This test runs in C++03 mode. Which is weird because it can use a lot of C++11 features; but constexpr isn't one of them. In C++11 this is a great improvement over my messy test. But the mess is needed for C++03. |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.fail.cpp | ||
---|---|---|
9 | Add // UNSUPPORTED: c++98, c++03 here to disable the test before 11. |
test/std/utilities/meta/meta.trans/meta.trans.other/underlying_type.pass.cpp | ||
---|---|---|
37 | I thought about that but IIRC underlying_type wasn't added until 2011. I can update if needed though. |
We should mark P0340R3 as complete with this patch. This is done in libcxx/www/cxx2a_status.html.
I forgot about this patch, and went off and implemented this myself. Sorry about that.
Reviewed as D63574; committed as r364094.
I think I'd rather bury the boolean in an __impl class.