-
Notifications
You must be signed in to change notification settings - Fork 1.6k
<memory>(mostly): Uses-Allocator and guaranteed copy elision For piecewise construction #421
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
Conversation
miscco
left a comment
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.
Some minor comments
| #if _HAS_CXX20 | ||
| _Uses_allocator_construct(_Ptr, _Scoped_outermost(*this), inner_allocator(), _STD forward<_Types>(_Args)...); | ||
| #else // ^^^ _HAS_CXX20 / !_HAS_CXX20 vvv | ||
| _Uses_allocator_construct(_Ptr, _Scoped_outermost(*this), inner_allocator(), _STD forward<_Types>(_Args)...); |
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.
Maybe I am blind but arent thesetwo lines identical`?
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.
#if part changed, as in standard, in terms of uses_allocator_construction_args
stl/inc/xmemory
Outdated
| template <class> | ||
| struct _Is_pair : false_type {}; | ||
|
|
||
| template <class _Ty1, class _Ty2> | ||
| struct _Is_pair<pair<_Ty1, _Ty2>> : true_type {}; |
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.
You are only ever using the value of _Is_pair so a variable template be better as that does not require a class instantiation
| template <class> | |
| struct _Is_pair : false_type {}; | |
| template <class _Ty1, class _Ty2> | |
| struct _Is_pair<pair<_Ty1, _Ty2>> : true_type {}; | |
| // VARIABLE TEMPLATE _Is_pair_v | |
| template <class> | |
| inline constexpr bool _Is_pair_v = false; | |
| template <class _Ty1, class _Ty2> | |
| inline constexpr bool _Is_pair_v<pair<_Ty1, _Ty2>> = false; |
stl/inc/xmemory
Outdated
| auto uses_allocator_construction_args(const _Alloc& _Al, _Types&&... _Args) { | ||
| if constexpr (!uses_allocator_v<_Ty, _Alloc>) { | ||
| static_assert(is_constructible_v<_Ty, _Types...>, | ||
| "uses_allocator_v<_Ty, _Alloc> is false but _Ty is not constructible from _Types..."); |
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.
Generally the error message uses the non-ugly names from the standard, as the user should not have to read the implementation to understand the message. Also I would suggest to formulate it differently along the lines of
T must be constructible from Types... if uses_allocator_v<T, Alloc> is false
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.
Hmmm I think it depends on whether we expect a template instantiation stack to be shown to the user. If the compiler is going to add:
with
[
_Ugly = sometype
]
to the output IMO it's more confusing to use the pretty name.
stl/inc/xmemory
Outdated
| return _STD forward_as_tuple(_STD forward<_Types>(_Args)...); | ||
| } else { | ||
| if constexpr (is_constructible_v<_Ty, allocator_arg_t, _Types...>) { | ||
| using _Return_type = _STD tuple<allocator_arg_t, const _Alloc&, _Types&&...>; |
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.
The _STD qualifier is unecessary as tuple is a type and not a function
stl/inc/xmemory
Outdated
| return _STD forward_as_tuple(_STD forward<_Types>(_Args)..., _Al); | ||
| } else { | ||
| static_assert( | ||
| _Always_false<_Ty>, "uses_allocator_v<_Ty, _Alloc> is true but _Ty is not constructible with _Alloc"); |
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.
Similar to above, the names should be non ugly.
stl/inc/xmemory
Outdated
| _STD forward<_Tuple2>(_Tup2))); | ||
| } | ||
|
|
||
| template <class _Ty, class _Alloc, enable_if_t<_Is_pair<_Ty>::value, int> = 0> |
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.
| template <class _Ty, class _Alloc, enable_if_t<_Is_pair<_Ty>::value, int> = 0> | |
| template <class _Ty, class _Alloc, enable_if_t<_Is_pair_v<_Ty>, int> = 0> |
stl/inc/xmemory
Outdated
| uses_allocator_construction_args<typename _Ty::second_type>(_Al)); | ||
| } | ||
|
|
||
| template <class _Ty, class _Alloc, class _Uty1, class _Uty2, enable_if_t<_Is_pair<_Ty>::value, int> = 0> |
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.
| template <class _Ty, class _Alloc, class _Uty1, class _Uty2, enable_if_t<_Is_pair<_Ty>::value, int> = 0> | |
| template <class _Ty, class _Alloc, class _Uty1, class _Uty2, enable_if_t<_Is_pair_v<_Ty>, int> = 0> |
stl/inc/xmemory
Outdated
| uses_allocator_construction_args<typename _Ty::second_type>(_Al, _STD forward<_Uty2>(_Val2))); | ||
| } | ||
|
|
||
| template <class _Ty, class _Alloc, class _Uty1, class _Uty2, enable_if_t<_Is_pair<_Ty>::value, int> = 0> |
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.
| template <class _Ty, class _Alloc, class _Uty1, class _Uty2, enable_if_t<_Is_pair<_Ty>::value, int> = 0> | |
| template <class _Ty, class _Alloc, class _Uty1, class _Uty2, enable_if_t<_Is_pair_v<_Ty>, int> = 0> |
stl/inc/xmemory
Outdated
| uses_allocator_construction_args<typename _Ty::second_type>(_Al, _Pair.second)); | ||
| } | ||
|
|
||
| template <class _Ty, class _Alloc, class _Uty1, class _Uty2, enable_if_t<_Is_pair<_Ty>::value, int> = 0> |
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.
| template <class _Ty, class _Alloc, class _Uty1, class _Uty2, enable_if_t<_Is_pair<_Ty>::value, int> = 0> | |
| template <class _Ty, class _Alloc, class _Uty1, class _Uty2, enable_if_t<_Is_pair_v<_Ty>, int> = 0> |
|
|
||
| template <class _Ty, class _Alloc, enable_if_t<_Is_pair<_Ty>::value, int> = 0> | ||
| auto uses_allocator_construction_args(const _Alloc& _Al) { | ||
| // equivalent to |
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.
Why are you not returning whats the equivalent call? This applies to all functions. As I can see it it involves more function calls.
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.
Isn't it good to have less function template instantiations? And no, it involves one less uses_allocator_construction_args and 2 apply calls in all cases
|
This also requires changes to |
stl/inc/xmemory
Outdated
| template <class _Ty1, class _Ty2> | ||
| inline constexpr bool _Is_pair_v<pair<_Ty1, _Ty2>> = true; | ||
|
|
||
| template <class _Ty, class _Alloc, class... _Types, enable_if_t<!_Is_pair_v<_Ty>, int> = 0> |
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.
It's a c++20 feature. Can't we use concepts there instead of SFINAE?
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.
Not sure if it is allowed, in the standard template parameters are plain classes. Plus we'd need 2 concepts, Pair and NotPair. Anyway, I think this sfinae is rather simple, but I'd rewrite it if concepts are faster in terms of compilation speed
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.
We don't need to make any concepts we can just use requires keyword.
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.
Moreover the standard uses the word Constraints
Constraints: T is not a specialization of pair.
which evidently suggests the use of concepts.
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 think concepts are generally preferable in newer code as they are simpler and provide better error messages.
Nevertheless whether you use a concept or SFINAE is your choice.
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.
SFINAE is horrible. Working with it is one of my biggest nightmares.
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.
It's a c++20 feature. Can't we use concepts there instead of SFINAE?
Sadly not all the frontends we support have concepts yet.
SFINAE is horrible. Working with it is one of my biggest nightmares.
concepts are mostly SFINAE :)
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.
Fair. I'll keep _Is_pair and replace enable_ifs with requires
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.
Sadly not all the frontends we support have concepts yet.
Yet. Someday they will.
concepts are mostly SFINAE :)
But 100x simpler and easier to debug.
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.
Sadly not all the frontends we support have concepts yet.
So enable_if remains for now?
|
Sorry, wrong branch |
stl/inc/xmemory
Outdated
| #if _HAS_CXX20 | ||
|
|
||
| template <class> | ||
| struct _Is_pair : false_type {}; |
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 think this is _Is_specialization<T, pair>.
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.
Didn't knew this existed, will try
stl/inc/xmemory
Outdated
| auto uses_allocator_construction_args(const _Alloc& _Al, _Types&&... _Args) { | ||
| if constexpr (!uses_allocator_v<_Ty, _Alloc>) { | ||
| static_assert(is_constructible_v<_Ty, _Types...>, | ||
| "uses_allocator_v<_Ty, _Alloc> is false but _Ty is not constructible from _Types..."); |
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.
Hmmm I think it depends on whether we expect a template instantiation stack to be shown to the user. If the compiler is going to add:
with
[
_Ugly = sometype
]
to the output IMO it's more confusing to use the pretty name.
stl/inc/xmemory
Outdated
| template <class _Ty1, class _Ty2> | ||
| inline constexpr bool _Is_pair_v<pair<_Ty1, _Ty2>> = true; | ||
|
|
||
| template <class _Ty, class _Alloc, class... _Types, enable_if_t<!_Is_pair_v<_Ty>, int> = 0> |
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.
It's a c++20 feature. Can't we use concepts there instead of SFINAE?
Sadly not all the frontends we support have concepts yet.
SFINAE is horrible. Working with it is one of my biggest nightmares.
concepts are mostly SFINAE :)
|
/azp run |
|
Azure Pipelines successfully started running 1 pipeline(s). |
|
Looks like there are some merge conflicts to resolve, and the new functionality needs some test coverage. |
|
Ping - would you be able to add test coverage here? If not, we can take care of getting this PR ready to merge. |
|
Thanks for implementing these features - and apologies for not reviewing this earlier. @AdamBucior has picked this up in #1668 with test coverage, so we'll continue with that PR. |
Description
Resolves #19 and resolves #20. I mostly copied standard quotes, unrolled some function calls and _Uglified. Now I wonder if it is possible to merge some code between
uses_allocator_construction_argsand_Uses_allocator_construct(fromxpolymorphic_allocator.h), their purpose is same it seems.Checklist
Be sure you've read README.md and understand the scope of this repo.
If you're unsure about a box, leave it unchecked. A maintainer will help you.
_Uglyas perhttps://eel.is/c++draft/lex.name#3.1 or there are no product code changes.
verified by an STL maintainer before automated testing is enabled on GitHub,
leave this unchecked for initial submission).
members, adding virtual functions, changing whether a type is an aggregate
or trivially copyable, etc.).
the C++ Working Draft (including any cited standards), other WG21 papers
(excluding reference implementations outside of proposed standard wording),
and LWG issues as reference material. If they were derived from a project
that's already listed in NOTICE.txt, that's fine, but please mention it.
If they were derived from any other project (including Boost and libc++,
which are not yet listed in NOTICE.txt), you must mention it here,
so we can determine whether the license is compatible and what else needs
to be done.