I have the following code (https://godbolt.org/z/K7sPbjjKE):
#include <string>
#include <ranges>
#include <vector>
std::vector<std::string> fields;
void insert_many(std::size_t pos, std::span<std::string> keys)
{
auto view = std::views::iota(0uz, keys.size())
| std::views::transform([&](std::size_t i) {
return std::move(keys[i]);
});
static_assert(std::ranges::input_range<decltype(view)>);
fields.insert(fields.cbegin() + pos, view.begin(), view.end());
}
Note: This example is oversimplified from https://godbolt.org/z/hYTjsohTf.
I know, you don't have to use std::views::iota
at all here. The question is about why the code in its current form doesn't work.
In both libc++ and libstdc++, the static_assert
passes.
However, only libc++ allows the call to insert
.
I am attempting to call
template< class InputIt > constexpr iterator insert( const_iterator pos, InputIt first, InputIt last );
From the looks of the error message (with libstdc++), std::ranges::transform_view::iterator
does not satisfy InputIterator so the overload cannot be called:
/opt/compiler-explorer/gcc-snapshot/lib/gcc/x86_64-linux-gnu/15.0.0/../../../../include/c++/15.0.0/bits/stl_vector.h:1484:2: note: candidate template ignored: requirement 'is_convertible<std::output_iterator_tag, std::input_iterator_tag>::value' was not satisfied [with > _InputIterator = _Iterator<false>] 1484 | insert(const_iterator __position, _InputIterator __first, | ^
Is that the intended behavior? I thought that the new std::views
stuff also satisfies the legacy iterator requirements.
Both libstdc++ and libc++ are correct. The code in question is simply relying on unspecified behavior and thus is non-portable.
std::views::iota(0uz, keys.size())
is required to be IOTA-DIFF-T(size_t)
, which is an unspecified signed-integer-like type ([range.iota.view]/(1.3)).signed_integral
type or a signed-integer-class type ([iterator.concept.winc]/4).That is, the standard doesn't require std::views::iota(0uz, keys.size())
to be an integer type.
Since the difference type of a transform_view
is the same as that of its underlying view, it's also not required to be an integer type. Thus, libstdc++ is correct to use the implementation-specific __int128
as the difference type.
On the other hand, since the standard doesn't forbid the use of integer type, libc++ is correct as well.