我有以下代码(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
东西也满足了遗留迭代器的要求。
libc++ 是正确的,这是一个 libstdc++ bug。
std::vector::insert
要求 InputIt
是 LegacyInputIterator:
template <class T>
concept __Referenceable = std::same_as<T&, std::add_lvalue_reference_t<T>>;
template <class I>
concept __LegacyIterator = requires(I i) {
{ *i } -> __Referenceable;
{ ++i } -> std::same_as<I&>;
{ *i++ } -> __Referenceable;
} && std::copyable<I>;
template <class I>
concept __LegacyInputIterator =
__LegacyIterator<I> && std::equality_comparable<I> && requires(I i) {
typename std::incrementable_traits<I>::difference_type;
typename std::indirectly_readable_traits<I>::value_type;
typename std::common_reference_t<
std::iter_reference_t<I>&&,
typename std::indirectly_readable_traits<I>::value_type&>;
*i++;
typename std::common_reference_t<
decltype(*i++)&&,
typename std::indirectly_readable_traits<I>::value_type&>;
requires std::signed_integral<
typename std::incrementable_traits<I>::difference_type>;
};
...
static_assert(__LegacyInputIterator<decltype(view.begin())>);
无法编译,libstdc++ 中出现以下错误:
<source>:38:17: error: static assertion failed
38 | static_assert(__LegacyInputIterator<decltype(view.begin())>);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
<source>:38:17: note: because 'decltype(view.begin())' (aka '_Iterator<false>') does not satisfy '__LegacyInputIterator'
<source>:27:16: note: because 'typename std::incrementable_traits<_Iterator<false>>::difference_type' (aka '__int128') does not satisfy 'signed_integral'
std::is_integral
,它允许:
任何实现定义的扩展整数类型
至此,我们可以得出结论,允许
__int128
作为difference_type
是无效的,因为它不是整型。 libstdc++ 应该更改此迭代器的 difference_type
,或者应该将 __int128
视为有符号整数。
出于好奇,我添加了以下两个值模板专业化(我知道这被认为是 UB):
template <>
inline constexpr bool std::is_integral_v<__int128> = true;
template <>
inline constexpr bool std::is_signed_v<__int128> = true;
这使得原始代码可以编译。
将
std::views::iota(0uz, keys.size())
更改为 std::views::iota(0u, static_cast<unsigned>(keys.size()))
也允许代码编译,并且不是 UB,但也不是解决此问题的非常令人满意的解决方法。