我有以下代码(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());
}
注意:此示例来自https://godbolt.org/z/hYTjsohTf。std::views::iota
我知道,你根本不必在这里使用。问题是为什么当前形式的代码不起作用。
在 libc++ 和 libstdc++ 中,都static_assert
通过了。然而,只有 libc++ 允许调用insert
. 我正在尝试打电话
template< class InputIt > constexpr iterator insert( const_iterator pos, InputIt first, InputIt last );
从错误消息(使用libstdc++)来看,std::ranges::transform_view::iterator
不满足InputIterator,因此无法调用重载:
/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, | ^
这是预期的行为吗?我认为新std::views
东西也满足了遗留迭代器的要求。
libstdc++ 和 libc++ 都是正确的。有问题的代码只是依赖于未指定的行为,因此是不可移植的。
std::views::iota(0uz, keys.size())
必须为IOTA-DIFF-T(size_t)
,这是一个未指定的类似有符号整数的类型([range.iota.view]/(1.3))。signed_integral
类型,也可以是有符号整数类类型( [iterator.concept.winc]/4 )。也就是说,标准不需要
std::views::iota(0uz, keys.size())
是整数类型。由于 a 的差异类型
transform_view
与其基础视图的差异类型相同,因此它也不需要是整数类型。因此,libstdc++ 使用特定于实现__int128
作为差异类型是正确的。另一方面,由于标准并不禁止使用整数类型,因此 libc++ 也是正确的。
libc++ 是正确的,但我认为这应该被视为 libstdc++ 错误,即使它在技术上满足P1522的要求。
std::vector::insert
要求InputIt
是LegacyInputIterator:编译失败,libstdc++ 中出现以下错误:
看看
std::is_integral
,它允许:此时,我们可以得出结论,为了允许
__int128
作为LegacyInputIteratordifference_type
的, libstdc++ 应该考虑为有符号整数。__int128
出于好奇,我添加了以下两个值模板专业化(我知道这被认为是 UB):
这使得原始代码可以编译。
更改
std::views::iota(0uz, keys.size())
为std::views::iota(0u, static_cast<unsigned>(keys.size()))
也允许代码编译,并且不是 UB,但也不是解决此问题的非常令人满意的方法。这里的问题是 std::views::transform 生成的迭代器类型并不完全是插入函数所期望的。它需要一个输入迭代器,但生成的更像是一个前向迭代器。
为了解决这个问题,我们需要使用 std::ranges::begin 和 std::ranges::end 显式地将视图的迭代器转换为输入迭代器。这样,我们就可以确定插入函数将正确接受该范围。