std::ranges::transform_view::iterator 不是一个InputIterator吗?

问题描述 投票:0回答:1

我有以下代码(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
东西也满足了遗留迭代器的要求。

c++ iterator stdvector std-ranges c++23
1个回答
0
投票

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,但也不是解决此问题的非常令人满意的解决方法。

© www.soinside.com 2019 - 2024. All rights reserved.