Document #: | P3698R0 [Latest] [Status] |
Date: | 2025-05-19 |
Project: | Programming Language C++ |
Audience: |
LEWG |
Reply-to: |
Charles Hussong <charles.hussong@proton.me> |
inplace_vector
, which was
proposed by [P0843R14] and accepted for C++26, has a
missing feature which weakens its API compared to ordinary
vector
: it is not possible to
compare instances with different capacities. I believe this should be
added before its standardization is completed.
[P0843R14] describes the API of
inplace_vector
as “closely
resembl[ing] std::vector<T, A>
”,
which indeed is largely the case, including its comparison operators.
Like vector
,
inplace_vector
has comparison
operators which look at the current contents, returning a lexicographic
ordering following the convention of [container.reqmts].
For vector
, the current capacity
is a dynamic member variable which is ignored for the purposes of
comparisons; therefore, the following code works regardless of the
internal logic for growing the capacity:
#include <cassert>
#include <vector>
int main() {
::vector<int> a{1, 2, 3};
std::vector<int> b{1, 2, 3};
std.reserve(10);
a.reserve(100);
bassert(a == b);
}
However, for inplace_vector
, the
capacity is a static part of the type itself and the comparison operator
is defaulted, so the equivalent code does not work:
#include <cassert>
#include <inplace_vector>
int main() {
::inplace_vector<int, 10> x{1, 2, 3};
std::inplace_vector<int, 100> y{1, 2, 3};
stdassert(x == y); // compilation error: decltype(x) not comparable with decltype(y)
}
The two vectors with different capacities are distinct types, so the defaulted comparison operator does not support a mixture of them.
This discrepancy with vector
does
not appear in the “Summary of semantic differences with
vector
” section of [P0843R14], leading one to surmise that
the intention was for both types to have the same comparison semantics.
Private conversations with the [P0843R14] authors confirm that they
intended for cross-capacity comparisons to work.
The original proposal [P0843R14] cites three library implementations: Boost.Container [1], EASTL [2], and Folly [3]. This paper was prompted by a user question related to an analogous class in ETL [4], so I have included it as well.
Of these,
vector
s of all
capacities [9]Therefore of the prior art, mixed-capacity comparisons work in ETL, do not work in EASTL and Folly, and Boost claims they work but in fact they do not.
From the current draft wording in [N5008], modify the comparison operators in the listing in section [inplace.vector.overview] to be templated on the capacity:
template <size_t M>
constexpr friend bool operator==(const inplace_vector& x, const inplace_vector<T, M>
& y);
template <size_t M>
constexpr friend synth-three-way-result<T>
operator<=>(const inplace_vector& x, const inplace_vector<T, M>
& y);
After [inplace.vector.erasure], add a new section [containers.sequences.inplace.vector.comparison] with the following contents:
template <size_t M> constexpr friend bool operator==(const inplace_vector& c, const inplace_vector<T, M>& b);
Effects: Equivalent to
c == b
defined in [container.reqmts], treatinginplace_vector<T, N>
andinplace_vector<T, M>
as the same type.
template <size_t M> constexpr friend synth-three-way-result<T> operator<=>(const inplace_vector& a, const inplace_vector<T, M>& b);
Effects: Equivalent to
a <=> b
defined in [container.opt.reqmts], treatinginplace_vector<T, N>
andinplace_vector<T, M>
as the same type.
There is another paper, [P0805R2], which attempts to generically
solve the issue of comparisons between containers which are logically
comparable but are not comparable in practice because they are distinct
types. Last updated in 2018, this paper would allow comparisons between
vectors with different allocator types or arrays with different sizes,
among other things. I support this paper as well, but it is much larger
in scope and therefore would require more effort and debate to adopt.
Since inplace_vector
is new for
C++26, I would like to fix its API before it’s released into the wild,
and then come back and try to merge the generic solution via an updated
[P0805R2] targeting C++29.
The changes proposed by this paper would not conflict with those of [P0805R2], which solves the problem mainly by amending [sequence.reqmts] to be more generic. The API that would be enabled by [P0805R2] would be a superset of the one proposed in this paper, and an updated [P0805R2] could simply widen the templating on the comparison operators proposed here to cover data types as well, and delete the section [containers.sequences.inplace.vector.comparison] which would no longer be needed. This would not break code that works under this paper’s wording.
Thank you to Anthony Williams for help with drafting this paper and Timur Doumler for insight into the intent behind [P0843R14].