<table border="1" cellspacing="0" cellpadding="8">
<tr>
<th>Issue</th>
<td>
<a href=https://github.com/llvm/llvm-project/issues/109996>109996</a>
</td>
</tr>
<tr>
<th>Summary</th>
<td>
[libc++] `std::equal` possibly redundant unwrap of iterators
</td>
</tr>
<tr>
<th>Labels</th>
<td>
libc++
</td>
</tr>
<tr>
<th>Assignees</th>
<td>
</td>
</tr>
<tr>
<th>Reporter</th>
<td>
akukh
</td>
</tr>
</table>
<pre>
Attempting to call `std::equal` on two ranges of elements for comparison fails in constant expression. See the following example (sorry in advance for a lot of code):
<details>
<summary>Code</summary>
```c++
#include <utility>
#include <algorithm>
#include <array>
#include <vector>
struct nontrivial_type {
constexpr nontrivial_type() noexcept {}
};
template <typename T>
union [[nodiscard]] uninitialized_storage final {
using value_type = T;
value_type value;
nontrivial_type _;
constexpr uninitialized_storage() : _{} {}
constexpr uninitialized_storage(uninitialized_storage const& other) noexcept
: value{other.value} {}
constexpr uninitialized_storage(uninitialized_storage&& other) noexcept
: value{std::move(other.value)} {}
constexpr uninitialized_storage& operator=(uninitialized_storage const&) noexcept = delete;
constexpr uninitialized_storage& operator=(uninitialized_storage&&) noexcept = delete;
constexpr ~uninitialized_storage() {}
};
struct nontrivial_int {
int x;
constexpr nontrivial_int() noexcept : x{0} {}
constexpr nontrivial_int(int val) noexcept : x{val} {}
constexpr ~nontrivial_int() {}
constexpr nontrivial_int(nontrivial_int const& other) noexcept {
x = other.x;
}
constexpr nontrivial_int(nontrivial_int&& other) noexcept {
x = other.x;
}
constexpr nontrivial_int& operator=(nontrivial_int const& other) noexcept {
x = other.x;
return *this;
}
constexpr nontrivial_int& operator=(nontrivial_int&& other) noexcept {
x = other.x;
return *this;
}
constexpr bool operator==(nontrivial_int const& other) const noexcept {
return x == other.x;
}
};
template <typename It, typename Fn>
class iterator_adapter final {
using functor_type = Fn;
public:
using reference = decltype(::std::declval<Fn>()(*::std::declval<It>()));
using value_type = ::std::remove_reference_t<reference>;
using pointer = ::std::add_pointer_t<value_type>;
using difference_type = ::std::iter_difference_t<It>;
using iterator_type = It;
using iterator_category = ::std::random_access_iterator_tag;
using iterator_concept = ::std::contiguous_iterator_tag;
iterator_type it_;
functor_type functor_;
constexpr iterator_adapter() noexcept : it_{}, functor_{} {}
constexpr iterator_adapter(iterator_type const& it, functor_type adapter = {}) noexcept
: it_{it}, functor_{adapter} {}
[[nodiscard]] constexpr reference operator*() const noexcept {
return *operator->();
}
[[nodiscard]] constexpr pointer operator->() const noexcept {
return ::std::addressof(functor_(*it_));
}
[[nodiscard]] constexpr reference operator[](difference_type index) const noexcept {
return functor_(it_[index]);
}
constexpr iterator_adapter& operator++() noexcept {
++it_;
return *this;
}
constexpr iterator_adapter operator++(int) noexcept {
return {it_++, functor_};
}
constexpr iterator_adapter& operator--() noexcept {
--it_;
return *this;
}
constexpr iterator_adapter operator--(int) noexcept {
return {it_--, functor_};
}
constexpr iterator_adapter& operator+=(difference_type n) noexcept {
it_ += n;
return *this;
}
constexpr iterator_adapter& operator-=(difference_type n) noexcept {
it_ -= n;
return *this;
}
constexpr iterator_adapter operator+(difference_type n) const noexcept {
return {it_ + n, functor_};
}
constexpr iterator_adapter operator-(difference_type n) const noexcept {
return {it_ - n, functor_};
}
[[nodiscard]] constexpr difference_type
operator-(iterator_adapter const& other) const noexcept {
return it_ - other.it_;
}
[[nodiscard]] constexpr std::strong_ordering
operator<=>(iterator_adapter const& other) const noexcept {
return it_ <=> other.it_;
}
[[nodiscard]] constexpr bool operator==(iterator_adapter const& other) const noexcept {
return it_ == other.it_;
}
friend constexpr auto operator+(difference_type n, iterator_adapter const& a) noexcept {
return iterator_adapter{a.it_ + n, a.functor_};
}
};
template <typename T, ::std::size_t N>
struct static_vector final {
struct [[nodiscard]] mapper final {
static constexpr T& operator()(uninitialized_storage<T>& storage) noexcept {
return storage.value;
}
static constexpr T const& operator()(uninitialized_storage<T> const& storage) noexcept {
return storage.value;
}
};
using iterator = iterator_adapter<
typename ::std::array<uninitialized_storage<T>, N>::iterator,
mapper
>;
using const_iterator = iterator_adapter<
typename ::std::array<uninitialized_storage<T>, N>::const_iterator,
mapper
>;
::std::array<uninitialized_storage<T>, N> storage_;
::std::size_t size_;
constexpr static_vector() noexcept : size_{} {
if consteval {
::std::construct_at(::std::addressof(storage_));
}
}
constexpr iterator begin() noexcept {
return {storage_.begin()};
}
constexpr const_iterator begin() const noexcept {
return {storage_.begin()};
}
constexpr iterator end() noexcept {
return {storage_.begin() + size_};
}
constexpr const_iterator end() const noexcept {
return {storage_.begin() + size_};
}
template <typename... Args>
constexpr T& emplace_back(Args&&... args) noexcept {
::std::construct_at(end().operator->(), ::std::forward<Args>(args)...);
++size_;
return storage_[size_ - 1].value;
}
template <::std::size_t OtherCapacity>
constexpr bool operator==(static_vector<T, OtherCapacity> const& other) const noexcept {
return ::std::equal(begin(), end(), other.begin());
}
};
int main() {
constexpr auto result = [] {
static_vector<nontrivial_int, 3> v1;
v1.emplace_back(1);
v1.emplace_back(2);
static_vector<nontrivial_int, 3> v2;
v2.emplace_back(1);
v2.emplace_back(2);
return v1 == v2;
}();
static_assert(true == result);
}
```
</details>
Fails with the following errors:
<details>
<summary>Output</summary>
```
<source>:203:20: error: constexpr variable 'result' must be initialized by a constant expression
203 | constexpr auto result = [] {
| ^ ~~~~
204 | static_vector<nontrivial_int, 3> v1;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
205 | v1.emplace_back(1);
| ~~~~~~~~~~~~~~~~~~~
206 | v1.emplace_back(2);
| ~~~~~~~~~~~~~~~~~~~
207 |
208 | static_vector<nontrivial_int, 3> v2;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
209 | v2.emplace_back(1);
| ~~~~~~~~~~~~~~~~~~~
210 | v2.emplace_back(2);
| ~~~~~~~~~~~~~~~~~~~
211 |
212 | return v1 == v2;
| ~~~~~~~~~~~~~~~~
213 | }();
| ~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__algorithm/comp.h:25:16: note: member call on dereferenced one-past-the-end pointer is not allowed in a constant expression
25 | return __x == __y;
| ^
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__algorithm/equal.h:42:10: note: in call to '__pred.operator()<nontrivial_int, nontrivial_int>(*(&v1.storage_.__elems_[0].value + 1), *(&v2.storage_.__elems_[0].value + 1))'
42 | if (!__pred(*__first1, *__first2))
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__algorithm/equal.h:61:10: note: in call to '__equal_iter_impl<const nontrivial_int *, const nontrivial_int *, std::__equal_to>(&v1.storage_.__elems_[0].value + 1, &v1.storage_.__elems_[2].value, &v2.storage_.__elems_[0].value + 1, __pred)'
61 | return std::__equal_iter_impl(
| ^~~~~~~~~~~~~~~~~~~~~~~
62 | std::__unwrap_iter(__first1), std::__unwrap_iter(__last1), std::__unwrap_iter(__first2), __pred);
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/opt/compiler-explorer/clang-18.1.0/bin/../include/c++/v1/__algorithm/equal.h:68:10: note: in call to 'equal<iterator_adapter<const uninitialized_storage<nontrivial_int> *, static_vector<nontrivial_int, 3>::mapper>, iterator_adapter<const uninitialized_storage<nontrivial_int> *, static_vector<nontrivial_int, 3>::mapper>, std::__equal_to>({&v1.storage_.__elems_[0], {}}, {&v1.storage_.__elems_[2], {}}, {&v2.storage_.__elems_[0], {}}, {})'
68 | return std::equal(__first1, __last1, __first2, __equal_to());
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:198:16: note: in call to 'equal<iterator_adapter<const uninitialized_storage<nontrivial_int> *, static_vector<nontrivial_int, 3>::mapper>, iterator_adapter<const uninitialized_storage<nontrivial_int> *, static_vector<nontrivial_int, 3>::mapper>>({&v1.storage_.__elems_[0], {}}, {&v1.storage_.__elems_[2], {}}, {&v2.storage_.__elems_[0], {}})'
198 | return ::std::equal(begin(), end(), other.begin());
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:212:16: note: in call to 'v1.operator==<3UL>(v2)'
212 | return v1 == v2;
| ^~~~~~~~
<source>:203:29: note: in call to '[] {
static_vector<nontrivial_int, 3> v1;
v1.emplace_back(1);
v1.emplace_back(2);
static_vector<nontrivial_int, 3> v2;
v2.emplace_back(1);
v2.emplace_back(2);
return v1 == v2;
}.operator()()'
203 | constexpr auto result = [] {
| ^~~~
204 | static_vector<nontrivial_int, 3> v1;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
205 | v1.emplace_back(1);
| ~~~~~~~~~~~~~~~~~~~
206 | v1.emplace_back(2);
| ~~~~~~~~~~~~~~~~~~~
207 |
208 | static_vector<nontrivial_int, 3> v2;
| ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
209 | v2.emplace_back(1);
| ~~~~~~~~~~~~~~~~~~~
210 | v2.emplace_back(2);
| ~~~~~~~~~~~~~~~~~~~
211 |
212 | return v1 == v2;
| ~~~~~~~~~~~~~~~~
213 | }();
| ~~~
<source>:214:19: error: static assertion expression is not an integral constant expression
214 | static_assert(true == result);
| ^~~~~~~~~~~~~~
<source>:214:27: note: initializer of 'result' is not a constant expression
214 | static_assert(true == result);
| ^
<source>:203:20: note: declared here
203 | constexpr auto result = [] {
| ^
2 errors generated.
```
</details>
Compiler options:
```
-std=c++23 -stdlib=libc++
```
[godbolt](https://godbolt.org/z/xETjjdMhf)
GCC with their `libstdc++` compiles and runs successfully. In this question, is the problem with `libc++` or does `libstdc++` incorrectly implement `std::equal`?
</pre>
<img width="1px" height="1px" alt="" src="http://email.email.llvm.org/o/eJzsW0tv4zjy_zTMhbBhUfHrkEMix38M8N-dw86eBVqibc7IpIak3Mkc8tkXJPWiRMnKo3tngTYa7khiVf2qWEVWlWgsJT0xQh7A8gksd3e4UGcuHvAfxR_nuwNPXx8elSKXXFF2gorDBGcZBKuFVCkIH0H4SP4scAZWC8gZVN84FJidiIT8CElGLoQpCY9cwIRfciyo5AweMc0kpAwmnEmFmYLkJRdESsrZHP6LEKjOBB55lvFvWip5wZc8IxCgjeRCvGpSnF4xS4hhjWHGlRaY8JQAtNW4FjuweARhlBKlpYHw2d6CEISRLC4XLF5B-BxpijACaN_cK2nt92ph_yUAPel_9i4KKUuyIiWaW6FoRlWL0nmKsxMXVJ0vQ8-FwEO0V5IoLjqQpBJFoiDjTAl6pTiL1WtOIFg_VRpCa1ht1O4wgDYAbSHj5CUhuTJU613Jfr0D4VNblJ73DCuDRVMzfCHwtxpPwShn0PjNE-MplQkWKVjuwHIHC0YZVRRn9C-SxlJxgU8EHinDmQu1kHqKrzgrSKlIuNMyHCDt5-ZjrutB-k7XHnGXhWMWL7zSOCB8hLE1jGufd_Dxq2_oAFpBrs5EtCei5K1FW9XWT2bMvLxykXwCA0CrqQDqCL_wq-bXBoS2n7DOCvKcCGxce3fTWK6_hjuYkowod_K_RqS1zS15Pqlvo_40GmP9cKZMuSGib7yMynepe0EePsIXsH5aDMzZCB8t-oozLzt9f9Qx37ywbjlNj6hjmuEgcq2mPy9mAq3jvjgeMwD5hvCh6Jks-V2ad532uxhCfwRRhWAQoEd1ptKD9jM4P22zGyDbk9lz6APnmYNumiHNHR_SEoRBetO1Jm6ovyiAIlhf7lm9wSYZlhJSZeHHOMW5ImJ4Fz0WTCcM5T6p8WlmDoK8OGQ0qTOkhlaQIxFEp1TVx659SVblDWYvqDcF_USvAGFkAZvgNt-PgyN_Ua2R5l84lgm0cHRYCqL3pLjGHCsQRvWVFtJnnHPKtPUgHGGM0zQuBxqeDRo_05QeawgWsoepnsC4PbI2RZ9hPdl1LqRHDg9LsCInLl69VsIs5ZcYJwmRMm4449MoR86M0_s4Jpwpeip4McCutWc5elDVpGMdL62uRve3bgj4djgtw24tKGq4TkvjPPxdBeolgqo2e_OsCktjrxJBL7Fy3K4CS1UfboVgELU_3250aQK5XvfQY2mwwXWtt8ZWtLMmYsdW3FugquDrs7251vYjVJeK_AjQpjabWXa0TfurytdY0NTGAG268U5ZSl6maNGCqnEunyylZnoT75iftvZeW6B6Cjxniu0wJyDfv8GOguojMlnAGKhK9NoAK8naYbH7GhvNZjftM5v9ENsYJO-ziyb5DjbRtg59vs3GsVEVQ0sL2XezlzN5H4Y564D8Pp7uRzZ1yTVTrA2qyb5omlum-xJ0s3dju7XUdkCVhG3YPZ3eka27SlgNbMbejfEPQK93JKkEZ6eYi5QIyk4Nh6buiEy58Pzl6tScJ6p1Syd_ufTVmFuV04R5OApKWNoCiQvFb0de1I-GGjaetOr21qL1E547QYrn3lB4f0cVRd00R9K_SKzgP-tasOwTSYUVTWLbGfZVguU4_0xfcJ77C0hLqnm3LP2bu0-UFZ6_1xVGpjGMVrDufnltXBq3HDTvN3Jhtzvjh9ZywvcAbMi-GmZ_smGvuDI1Qs-vwshlWztGN_W1rwqi8QmIrNPUlac1TUeEdYTKX72VqDFU_N9C7kpv8DvATTH17LH6J8RXU95Zl7zROfYxQ7rQ2rtHK4595aylb8pXN6k5lqyuThz3q3W7GsRY9Rs47UKq1rlfP7l-Pin9gAdyouxmpt3kFpX4eYtwMLlwZXa8tC15em7zCfm1ZMLST2ps9pVy2n27yajijfjPqT0Awr81-3az-XwOH8Wp_cYVdrcUQ5aQ-ICTPwDamOGmS6yJsbnymnHcv2sTzD3Ni_4Oe-Tim94Zw6iCizal7Pl87oSBrUjdiO6Ys46gpR0HZzAAy51n4-jasW1E_zLzq06VIpzjpP2a2bWrP3NzVxm90KGox-4D_e8OTnsEAG3a4YOixif1hc33nBHbSSkTZQpecOOf_vfcJikURBaZfWlnOzat0V1TdF9SRDDUtrgGvRm-BvOOwwYO9v5z1H7uS2Ruw0At_mhcfgWzN8wHo5zAa1Dl4W1JpXf2230laiwlETrUlChIxcAa3RFVT2Z1eqINwR636J7LsN97czLkG1Xn7hEQIbiQowc7nHMdvxYqL9Skkx01R8kLUb48eESL0HzrndjI1n803nbFguKDOZKyrvRfw0shFTwQ2Eoy4OEVYt85l8q2aBFCsI7e48nllhz5sg6wfK7-fHt7eysp0OLeIfhoLGgebxM-jXJLQ3MrgnoajfJcDfBEn-C5NkNrc20-ZC70aXNtrWpTAn6qasFigOcnzBUEjrkC5FDeWmLG5TRCbGD4F6TauGX0oj3PFUD7hF9ymhExIy95xoXezfZJhtlpFmzmwXwB0P6gt5K93uH35VErPaZqOu-vAUD7OG6ObVmm87NeD5YgfAxWei1gXBH9_4VcDkTYU3GcwZTULw9SyBmZ5ViqmTqTGWFp_RaESk0PsV7dSGqOs42tERAta5OVxo3j-hV0HL96TAOWzz_CNGbnN7a5R9o2i7ZtKLN2UVyvk3GcC5LOO9W6L5o6d8oczr7DWl2DeZ23xjHJyEXqpGtRJ1smgw2qnK8mQ9PJtgCta3veN85Nj9AwC6wqFlUcH6mQKiillZeoZNSflZvrwI-asFVwc8LMYFNjxPSSZyCMqsTQPaykjRzBsWd1qlgxVbya1-kzqg08NBo1yXY5buqER7CaTmfeV0E573WC39GgMQvaTJ7nhn17zWzxLtg3gXPDHKBN41zbjhW74zI8aVjLOdt6t_LwaVvWZz4_2M03N9zcFi9h5GluWY8e6hr1lqnG2afkC-XZStvPsv2nvwGE4UBdP43Hqok7e2JhXV-MxusQxXDkeinMeQsndDcDoVvVqe01uw4d_WcVHvrvWv1-tTpxPb8VAW7JEWw3vezip6cOQPjbumTbEYOtdcTv1TT5mBv6690A3XC-azDvtJfCKPz3_9uZuCJX83eVBJ5StlForDrfDqP9ovbPJ1s_HyweJ9R_E8q5MZuD9a6biJff625jAr6vzdbxyYab7UD87Dx0ef7sPPwvdh5K-oG19N5s507vsHyBbTuolLNWmV-3AxikTJGTwNloxzBwQmliT3Z4sxjRAa3dFbba2AXkR7f1WakwBTh8d0PZ19YY7tdWeFOSZFiQFJ6JIN-v32pHobJFDU-E6XWVpPMP9sCjsh6CPNeO0mp6u5xmJpPYlWUQCqG-kdEDCHcZPbi_V-xgWD6deHrgmbKHWs9K5UYM2gO0Lx_NuTgBtP8LoP3L82-__57-43ysOxr2-_-iqO7VUwHBapHRg1RpJXu1gGVxJyFmKRQFk1AW5jj6sciy1zn8hUF1phL-WRCptTW5pTTN_1zwQ0YuVoLl3WLMBUw5kV6hlCVcCJKo7BXqAt38CtX7m1UQ7u_ShzDdhlt8Rx6CNVqj9SpYre7OD8lqEZDtcbvCqzUJj-khXIWbLTnebzb3YZre39EHtED3iy1aBvfL1WI13ySrY3pEGOF1uEHHENwvyAXTbJ5l14u25x2VsiAPwWK73a7uMnwgmTS_u0WopR3SieideNBUs0NxkuB-kVGpZMNHUZWZX-y2yLTP-n6Wm3Mp6SF7hYKkBUt1bNqugA7iKo-Xd4XIHjqOQNW5OMwTrgtqLbr8b5YL_jtJdO1u9JEA7UuVrg_oPwEAAP__KQUe6w">