Implement LWG-3545 std::pointer_traits should be SFINAE-friendly#3242
Implement LWG-3545 std::pointer_traits should be SFINAE-friendly#3242StephanTLavavej merged 3 commits intomicrosoft:mainfrom
std::pointer_traits should be SFINAE-friendly#3242Conversation
| template <class, class = void, class = void> | ||
| struct _Ptr_traits_sfinae_layer {}; | ||
|
|
||
| template <class _Ty, class _Uty> | ||
| struct _Ptr_traits_sfinae_layer<_Ty, _Uty, void_t<typename _Get_first_parameter<_Ty>::type>> | ||
| : _Ptr_traits_base<_Ty, typename _Get_first_parameter<_Ty>::type> {}; | ||
|
|
||
| template <class _Ty> | ||
| struct _Ptr_traits_sfinae_layer<_Ty, void_t<typename _Ty::element_type>, void> | ||
| : _Ptr_traits_base<_Ty, typename _Ty::element_type> {}; |
There was a problem hiding this comment.
So this seems to basically do as follows:
The third template specialization (right line 298) is the most specific of the three, so if it's valid, it'll be chosen, and our element_type will be _Ty::element_type.
If the third template specialization is not chosen, because _Ty::element_type is not a type name, then the second (right line 294) is still more specific than the first (right line 291), and if it is valid, we'll choose it.
Finally, otherwise, if _Ty is not a class template with only type arguments, we fall back to having no members.
This makes sense, and I think correctly implements the wording in the standard.
There was a problem hiding this comment.
Agreed. I believe it could have been written with both void_ts in the same position, but this way is fine too.
There was a problem hiding this comment.
I don't necessarily believe this is true, since if typename _Get_first_parameter<_Ty>::type and typename _Ty::element_type are both well formed (which they often are, like allocator<_Ty>), then both specializations are valid and equally specialized.
There was a problem hiding this comment.
To clarify, what I'm envisioning is having the partial specializations be <_Ty, void_t<ONE>, _Uty> and <_Ty, void_t<TWO>, void>. In that case, when ONE and TWO are both well-formed, the partial specialization with void as the last argument is more specialized than the one with _Uty as the last argument.
| template <class, class = void, class = void> | ||
| struct _Ptr_traits_sfinae_layer {}; | ||
|
|
||
| template <class _Ty, class _Uty> | ||
| struct _Ptr_traits_sfinae_layer<_Ty, _Uty, void_t<typename _Get_first_parameter<_Ty>::type>> | ||
| : _Ptr_traits_base<_Ty, typename _Get_first_parameter<_Ty>::type> {}; | ||
|
|
||
| template <class _Ty> | ||
| struct _Ptr_traits_sfinae_layer<_Ty, void_t<typename _Ty::element_type>, void> | ||
| : _Ptr_traits_base<_Ty, typename _Ty::element_type> {}; |
There was a problem hiding this comment.
Agreed. I believe it could have been written with both void_ts in the same position, but this way is fine too.
|
|
||
| STATIC_ASSERT(is_same_v<pointer_traits<Templated<char>>::element_type, char>); | ||
| STATIC_ASSERT(is_same_v<pointer_traits<Templated<char>>::pointer, Templated<char>>); | ||
| STATIC_ASSERT(is_same_v<pointer_traits<Templated<char>>::difference_type, char>); |
There was a problem hiding this comment.
No change requested: Because this test case says using difference_type = I;, it's unable to distinguish whether pointer_traits is reading the template argument (as it did for element_type) or the difference_type typedef (as desired, which it is indeed doing). Using a unique type for the difference_type, like int, would make the test slightly stronger and clearer.
Below, CheckPriority's using element_type = T[42]; is a good example of detecting what pointer_traits is looking at.
I mention this only for future reference, as there is essentially zero risk in this specific case. This is related to my general recommendation for test data, which is "unique numbers for unique purposes".
|
I'm mirroring this to the MSVC-internal repo - please notify me if any further changes are pushed. |
|
Thanks for helping SFINAE be everyone's friend! 😻 ✅ 🚀 |
Fixes #3237.