pointer_traits contains this static_assert: static_assert(!is_same<element_type, __undefined>::value, "pointer type defines element_type or is like SomePointer<T, Args>"); When trying to use address_of() with an iterator that has a usable operator->(), address_of() fails because the static_assert causes a hard error in the trailing decltype here: template<typename _Ptr> constexpr auto __to_address(const _Ptr& __ptr) noexcept -> decltype(std::pointer_traits<_Ptr>::to_address(__ptr)) { return std::pointer_traits<_Ptr>::to_address(__ptr); } template<typename _Ptr, typename... _None> constexpr auto __to_address(const _Ptr& __ptr, _None...) noexcept { if constexpr (is_base_of_v<__gnu_debug::_Safe_iterator_base, _Ptr>) return std::__to_address(__ptr.base().operator->()); else return std::__to_address(__ptr.operator->()); } When I comment out the static_assert, to_address() works with the iterator in question.
I think this is the "correct" behaviour. The specification of pointer_traits<Ptr>::element_type says that if Ptr::element_type isn't valid, and Ptr isn't a template that can be "rebound", then "otherwise, the specialization is ill-formed." This explicitly says "the specialization" (meaning pointer_traits<Ptr> itself). Compare this with th rebind member which only says "the instantiation of rebind is ill-formed".
Fair enough. [pointer.conversion] says that to_pointer(const Ptr& p) is "pointer_traits<Ptr>::to_address(p) if that expression is well-formed (see [pointer.traits.optmem]), otherwise to_address(p.operator->())". Do we then have a spec bug?
Ugh, yes, I think we do.
On reading this again, I think the implementation should be required to check whether the specialization pointer_traits<Ptr> would be well-formed as part of checking the expression pointer_traits<Ptr>::to_address(p). So it's unnecessarily awkward, but still the implementation's job to do that.
But on third thoughts, I don't know how to implement this reliably.
> Do we then have a spec bug? The to_address(const P&) overload always assumed a valid pointer_traits<P>. Even before it was std::to_address in C++20, when it was __to_address in libstdc++, or boost::to_address, or __to_raw_pointer in libc++, and was used in C++11 and above, its return type was 'typename std::pointer_traits<P>::element_type*' which requires a valid pointer_traits. i.e. Our allocator-aware containers would only ever work with fancy pointer P for which pointer_traits<P> is valid. std::to_address being used for more than just raw-or-fancy-pointers came later (since Casey's P1474 which chose to use it for contiguous iterators). My guess is they didn't realize pointer_traits<I> wouldn't be valid for those iterator types.
Thanks, Glen. So it seems I was right the first time, and using std::to_address does require a type that can be used with std::pointer_traits. Not a bug then. Sorry, Zach!
I suppose I could change the static_assert message to also suggest defining your own specialization of std::pointer_traits, which is another way to make it work.
Hi, Stumbled across this indeed when trying to use contiguous iterators. Sure enough, pointer_traits for them is ill-formed. But then I don't understand why the "otherwise to_address(p.operator->())" part is in the Standard at all, if it can never be used. Has this been raised as a library defect? And are you recommending that everyone who defines their custom contiguous iterators specializes pointer_traits for them? Call it _quite_ annoying...
(By the way, finding this bug is quite hard. Could "address_of" be changed to "to_address" , in the bug description? I think that's the intended meaning. And, "to_pointer", mentioned a few times, doesn't exist.)
> if it can never be used. You're misunderstanding. to_address(p) requires that pointer_traits<P> is valid. It just doesn't need to have a to_address member function. Example 1. You have a pointer-like type Ptr1. You haven't specialized pointer_traits<Ptr1>, but pointer_traits<Ptr1> is valid. Here it will call to_address(p1.operator->()). Example 2. You have a pointer-like type Ptr2. You have specialized pointer_traits<Ptr2> with a to_address function. Here it will call pointer_traits<Ptr2>::to_address(p2). to_address() was intended for used with pointers and pointer-like types (and pointer_traits<Ptr> was always required to be valid). It was intended for use with allocator pointers, and the original standard library implementations had a return type of: typename pointer_traits<Ptr>::element_type* If (for contiguous iterators, which came later) you want pointer_traits<X> to be valid even when X does not have element_type, that is a design change to pointer_traits.
(In reply to Giuseppe D'Angelo from comment #10) > (By the way, finding this bug is quite hard. Could "address_of" be changed > to "to_address" , in the bug description? Done.
> And are you recommending that everyone who defines their custom contiguous > iterators specializes pointer_traits for them? Call it _quite_ annoying... Definitely not! When you define a contiguous iterator type, you should just give it a sixth nested typedef alongside the other five (or three in C++20): `using element_type = value_type;`. This enables contiguous-iterator machinery. See https://stackoverflow.com/questions/65712091/in-c20-how-do-i-write-a-contiguous-iterator/66050521#66050521 You should never specialize std::pointer_traits for your own type. ("Can" you? Yes. "Should" you? No.)
Hello, (In reply to Glen Joseph Fernandes from comment #11) > > if it can never be used. > > You're misunderstanding. to_address(p) requires that pointer_traits<P> is > valid. It just doesn't need to have a to_address member function. Thank you for clarifying this. I think the wording in the standard is very unfortunate, but combined with the realization that pointer_traits isn't SFINAE-friendly, then it's the only intended meaning. > If (for contiguous iterators, which came later) you want pointer_traits<X> > to be valid even when X does not have element_type, that is a design change > to pointer_traits. One might claim that pointer_traits should become SFINAE-friendly (like C++17's iterator_traits), but sure, that's a different design question and not necessarily needed here. (In reply to Jonathan Wakely from comment #12) > (In reply to Giuseppe D'Angelo from comment #10) > > (By the way, finding this bug is quite hard. Could "address_of" be changed > > to "to_address" , in the bug description? > > Done. Thank you! (In reply to Arthur O'Dwyer from comment #13) > > And are you recommending that everyone who defines their custom contiguous > > iterators specializes pointer_traits for them? Call it _quite_ annoying... > > Definitely not! When you define a contiguous iterator type, you should just > give it a sixth nested typedef alongside the other five (or three in C++20): > `using element_type = value_type;`. This enables contiguous-iterator > machinery. > See > https://stackoverflow.com/questions/65712091/in-c20-how-do-i-write-a- > contiguous-iterator/66050521#66050521 This gets evil really quick: the presence of both value_type and element_type in an contiguous iterator will make you smash face-first against LWG3446, which isn't implemented in GCC 10 AFAICS. https://cplusplus.github.io/LWG/issue3446 What's more, the accepted resolution wording for it appears to be wrong: template<classhas-member-value-type T> requires has-member-element-type<T> && same_as<remove_cv_t<typename T::element_type>, remove_cv_t<typename T::value_type>> struct indirectly_readable_traits<T> : cond-value-type<typename T::value_type> { }; For const iterators, value_type is actually different from element_type (!). Thankfully libstdc++ seems to have considered this as a non-standard extension, https://github.com/gcc-mirror/gcc/commit/186aa6304570e15065f31482e9c27326a3a6679f To summarize: * should a wording defect be raised against std::to_address(Ptr), to state that pointer_traits<Ptr> being well-formed is actually a prerequisite? * should LWG3446's resolution be amended? * if there's going to be a GCC 10.3, is the commit above solving LWG3446 going to be cherry-picked into it? Otherwise, either one blacklists GCC 10, or has to specialize pointer_traits there as a workaround (?). Thank you all for the insightful comments.
(In reply to Giuseppe D'Angelo from comment #14) > This gets evil really quick: the presence of both value_type and > element_type in an contiguous iterator will make you smash face-first > against LWG3446, which isn't implemented in GCC 10 AFAICS. That's right, but it's on my list to backport to the gcc-10 branch, probably in the next 48 hours.
> should a wording defect be raised against std::to_address(Ptr), to state that > pointer_traits<Ptr> being well-formed is actually a prerequisite? That's not an omission in the specification of to_address. The function is intended for pointers, and specified in terms of checking for a pointer_traits<P> member, and this means pointer_traits<P> must be well-formed. Adding an additional text to the specification saying this explicitly is unlikely to help anyone. The real change that users of iterators[1] would want is to make pointer_traits SFINAE friendly. [1] Users of pointers don't care much, since all the pointer types people are using with to_address(p) already have a well-formed pointer_traits<P>.
(In reply to Giuseppe D'Angelo from comment #14) > To summarize: > > * should a wording defect be raised against std::to_address(Ptr), to state > that pointer_traits<Ptr> being well-formed is actually a prerequisite? I'd prefer if pointer_traits was just SFINAE friendly. > * should LWG3446's resolution be amended? See https://cplusplus.github.io/LWG/issue3541 > * if there's going to be a GCC 10.3, is the commit above solving LWG3446 > going to be cherry-picked into it? Otherwise, either one blacklists GCC 10, > or has to specialize pointer_traits there as a workaround (?). It missed the 10.3 release, but it's on the gcc-10 branch as r10-9698, which will be in GCC 10.4: https://gcc.gnu.org/g:32a859531e854382c18abf0b14a306d83f793eb5 That also includes the fix for LWG 3541.
Hello, (In reply to Jonathan Wakely from comment #17) > (In reply to Giuseppe D'Angelo from comment #14) > > To summarize: > > > > * should a wording defect be raised against std::to_address(Ptr), to state > > that pointer_traits<Ptr> being well-formed is actually a prerequisite? > > I'd prefer if pointer_traits was just SFINAE friendly. I guess that's a reasonable thing to wish for, given I'm not the first falling for it; I hope I'll be the last :) > > * should LWG3446's resolution be amended? > > See https://cplusplus.github.io/LWG/issue3541 > > > * if there's going to be a GCC 10.3, is the commit above solving LWG3446 > > going to be cherry-picked into it? Otherwise, either one blacklists GCC 10, > > or has to specialize pointer_traits there as a workaround (?). > > It missed the 10.3 release, but it's on the gcc-10 branch as r10-9698, which > will be in GCC 10.4: > https://gcc.gnu.org/g:32a859531e854382c18abf0b14a306d83f793eb5 > That also includes the fix for LWG 3541. Thank you very much for the new issue and the cherry-pick of the fix.
> I guess that's a reasonable thing to wish for, given I'm not the first > falling for it; I hope I'll be the last :) > Unfortunately not, I ran into the same issue :( But thanks to the thread, I got some insight into the question: Should every type with an "operator->()" really be considered a pointer?
Reopening and suspending, until https://wg21.link/lwg3545 is resolved.
The master branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>: https://gcc.gnu.org/g:b8018e5c5ec0e9b6948182f13fba47c67b758d8a commit r12-5532-gb8018e5c5ec0e9b6948182f13fba47c67b758d8a Author: Jonathan Wakely <jwakely@redhat.com> Date: Thu Nov 25 16:49:45 2021 +0000 libstdc++: Make std::pointer_traits SFINAE-friendly [PR96416] This implements the resolution I'm proposing for LWG 3545, to avoid hard errors when using std::to_address for types that make pointer_traits ill-formed. Consistent with std::iterator_traits, instantiating std::pointer_traits for a non-pointer type will be well-formed, but give an empty type with no member types. This avoids the problematic cases for std::to_address. Additionally, the pointer_to member is now only declared when the element type is not cv void (and for C++20, when the function body would be well-formed). The rebind member was already SFINAE-friendly in our implementation. libstdc++-v3/ChangeLog: PR libstdc++/96416 * include/bits/ptr_traits.h (pointer_traits): Reimplement to be SFINAE-friendly (LWG 3545). * testsuite/20_util/pointer_traits/lwg3545.cc: New test. * testsuite/20_util/to_address/1_neg.cc: Adjust dg-error line. * testsuite/20_util/to_address/lwg3545.cc: New test.
The releases/gcc-11 branch has been updated by Jonathan Wakely <redi@gcc.gnu.org>: https://gcc.gnu.org/g:8d3391d64799d490117ad48432a9ad2cf38b0091 commit r11-9317-g8d3391d64799d490117ad48432a9ad2cf38b0091 Author: Jonathan Wakely <jwakely@redhat.com> Date: Thu Nov 25 23:29:08 2021 +0000 libstdc++: Make std::pointer_traits SFINAE-friendly [PR96416] This is a simplified version of r12-5532 for the release branches. It still removes the problematic static_assert, but rather than making std::pointer_traits completely empty when the element_type can't be deduced, it just disables element_type and pointer_to. Additionally, the pointer_to member is not completely absent when element_type is cv void, it just has an unusable signature. This is sufficient to avoid errors outside the immediate context when trying to use std::to_address. libstdc++-v3/ChangeLog: PR libstdc++/96416 * include/bits/ptr_traits.h (pointer_traits): Remove static_assert checking for valid element_type. (pointer_traits::element_type, pointer_traits::pointer_to): Do not define when element type cannot be deduced. * testsuite/20_util/pointer_traits/lwg3545.cc: New test. * testsuite/20_util/to_address/1_neg.cc: Adjust dg-error line. * testsuite/20_util/to_address/lwg3545.cc: New test. (cherry picked from commit b8018e5c5ec0e9b6948182f13fba47c67b758d8a)
Fixed for 11.3 now. If any of you who hit this bug could test GCC trunk or the gcc-11 branch and confirm it works for your cases now, that would be much appreciated (there are no actual testcases here in this bug).
Sorry for the delay. I confirmed that this makes my case well-formed with releases/gcc-11, and that it's ill-formed with GCC 11.2 and GCC 10.x.
GCC 10.4 is being released, retargeting bugs to GCC 10.5.
Fixed in 11.3.