-
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
Implement LWG-3545 std::pointer_traits
should be SFINAE-friendly
#3242
Changes from all commits
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 |
---|---|---|
@@ -0,0 +1,4 @@ | ||
# Copyright (c) Microsoft Corporation. | ||
# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
||
RUNALL_INCLUDE ..\usual_matrix.lst |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
// Copyright (c) Microsoft Corporation. | ||
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception | ||
|
||
#include <cstddef> | ||
#include <memory> | ||
#include <type_traits> | ||
|
||
using namespace std; | ||
|
||
#define STATIC_ASSERT(...) static_assert(__VA_ARGS__, #__VA_ARGS__); | ||
|
||
template <class, class = void> | ||
constexpr bool has_memtype_element_type = false; | ||
|
||
template <class T> | ||
constexpr bool has_memtype_element_type<T, void_t<typename T::element_type>> = true; | ||
|
||
template <class, class = void> | ||
constexpr bool has_memtype_pointer = false; | ||
|
||
template <class T> | ||
constexpr bool has_memtype_pointer<T, void_t<typename T::pointer>> = true; | ||
|
||
template <class, class = void> | ||
constexpr bool has_memtype_difference_type = false; | ||
|
||
template <class T> | ||
constexpr bool has_memtype_difference_type<T, void_t<typename T::difference_type>> = true; | ||
|
||
STATIC_ASSERT(!has_memtype_element_type<pointer_traits<int>>); | ||
STATIC_ASSERT(!has_memtype_pointer<pointer_traits<int>>); | ||
STATIC_ASSERT(!has_memtype_difference_type<pointer_traits<int>>); | ||
|
||
struct LackElementType { | ||
using pointer = int; | ||
using difference_type = int; | ||
template <class> | ||
using rebind = int; | ||
}; | ||
|
||
STATIC_ASSERT(!has_memtype_element_type<pointer_traits<LackElementType>>); | ||
STATIC_ASSERT(!has_memtype_pointer<pointer_traits<LackElementType>>); | ||
STATIC_ASSERT(!has_memtype_difference_type<pointer_traits<LackElementType>>); | ||
|
||
struct OnlyElementType { | ||
using element_type = void; | ||
}; | ||
|
||
STATIC_ASSERT(has_memtype_element_type<pointer_traits<OnlyElementType>>); | ||
STATIC_ASSERT(has_memtype_pointer<pointer_traits<OnlyElementType>>); | ||
STATIC_ASSERT(has_memtype_difference_type<pointer_traits<OnlyElementType>>); | ||
|
||
STATIC_ASSERT(is_same_v<pointer_traits<OnlyElementType>::element_type, void>); | ||
STATIC_ASSERT(is_same_v<pointer_traits<OnlyElementType>::pointer, OnlyElementType>); | ||
STATIC_ASSERT(is_same_v<pointer_traits<OnlyElementType>::difference_type, ptrdiff_t>); | ||
|
||
template <class I> | ||
struct Templated { | ||
using difference_type = I; | ||
}; | ||
|
||
STATIC_ASSERT(has_memtype_element_type<pointer_traits<Templated<char>>>); | ||
STATIC_ASSERT(has_memtype_pointer<pointer_traits<Templated<char>>>); | ||
STATIC_ASSERT(has_memtype_difference_type<pointer_traits<Templated<char>>>); | ||
|
||
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. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. No change requested: Because this test case says Below, 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". |
||
|
||
template <class I, size_t N = sizeof(I)> | ||
struct BadTemplated { | ||
using difference_type = I; | ||
}; | ||
|
||
STATIC_ASSERT(!has_memtype_element_type<pointer_traits<BadTemplated<int>>>); | ||
STATIC_ASSERT(!has_memtype_pointer<pointer_traits<BadTemplated<int>>>); | ||
STATIC_ASSERT(!has_memtype_difference_type<pointer_traits<BadTemplated<int>>>); | ||
|
||
template <class T> | ||
struct CheckPriority { | ||
using element_type = T[42]; | ||
}; | ||
|
||
STATIC_ASSERT(has_memtype_element_type<pointer_traits<CheckPriority<char>>>); | ||
STATIC_ASSERT(has_memtype_pointer<pointer_traits<CheckPriority<char>>>); | ||
STATIC_ASSERT(has_memtype_difference_type<pointer_traits<CheckPriority<char>>>); | ||
|
||
STATIC_ASSERT(is_same_v<pointer_traits<CheckPriority<char>>::element_type, char[42]>); | ||
STATIC_ASSERT(is_same_v<pointer_traits<CheckPriority<char>>::pointer, CheckPriority<char>>); | ||
STATIC_ASSERT(is_same_v<pointer_traits<CheckPriority<char>>::difference_type, ptrdiff_t>); | ||
|
||
int main() {} // COMPILE-ONLY |
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 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.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed. I believe it could have been written with both
void_t
s in the same position, but this way is fine too.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 don't necessarily believe this is true, since if
typename _Get_first_parameter<_Ty>::type
andtypename _Ty::element_type
are both well formed (which they often are, likeallocator<_Ty>
), then both specializations are valid and equally specialized.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.
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, whenONE
andTWO
are both well-formed, the partial specialization withvoid
as the last argument is more specialized than the one with_Uty
as the last argument.