我想要查看输入流中的字符:
auto input = std::stringstream{"abcd"};
using Iter = std::istreambuf_iterator<char>;
auto s = std::ranges::subrange{Iter{input}, Iter{}};
到目前为止一切顺利。现在,我转换该视图(为了简单起见,使用身份转换):
auto t = s | std::views::transform(std::identity{});
尽管这个转换后的观点是有效的value_type
(即这个断言通过了):
using T = decltype(t);
static_assert(std::is_same_v<char, std::ranges::range_value_t<T>>);
它的迭代器类型没有:
static_assert(std::is_same_v<char, std::iterator_traits<std::ranges::iterator_t<T>>::value_type>);
这失败了
view.cc: In function 'int main()':
view.cc:27:90: error: 'value_type' is not a member of 'std::iterator_traits<std::ranges::transform_view<std::ranges::subrange<std::istreambuf_iterator<char, std::char_traits<char> >, std::istreambuf_iterator<char, std::char_traits<char> >, std::ranges::subrange_kind::unsized>, std::identity>::_Iterator<false> >'
27 | static_assert(std::is_same_v<char, std::iterator_traits<std::ranges::iterator_t<T>>::value_type>);
| ^~~~~~~~~~
view.cc:27:100: error: template argument 2 is invalid
27 | static_assert(std::is_same_v<char, std::iterator_traits<std::ranges::iterator_t<T>>::value_type>);
|
它对我很重要的原因是我想在下游视图适配器中使用迭代器特征。
调查表明,Transform 视图的迭代器缺少其iterator_category
成员。当我传递一个功能更强大的范围(例如,传递给 Transform 的范围)时,代码会成功,std::string
但传递这个非前向范围时会失败。
完整代码(也在Compiler Explorer 上):
#include <functional>
#include <iterator>
#include <ranges>
#include <sstream>
#include <string>
#include <type_traits>
int main()
{
#ifdef PASS
auto input = std::string{"abcd"};
std::ranges::forward_range auto s = std::ranges::subrange(input.begin(), input.end());
#else
auto input = std::stringstream{"abcd"};
using Iter = std::istreambuf_iterator<char>;
std::ranges::input_range auto s = std::ranges::subrange{Iter{input}, Iter{}};
#endif
using S = decltype(s);
static_assert(std::is_same_v<char, std::ranges::range_value_t<S>>);
static_assert(std::is_same_v<char, std::iterator_traits<std::ranges::iterator_t<S>>::value_type>);
auto t = s | std::views::transform(std::identity{});
using T = decltype(t);
static_assert(std::is_same_v<char, std::ranges::range_value_t<T>>);
static_assert(std::is_same_v<char, std::ranges::iterator_t<T>::value_type>);
static_assert(std::is_base_of_v<std::input_iterator_tag, std::ranges::iterator_t<T>::iterator_category>);
static_assert(std::is_same_v<char, std::iterator_traits<std::ranges::iterator_t<T>>::value_type>);
}
当
transform_view
应用于底层范围时,其迭代器的后增量运算符(operator++(int)
)仅当底层范围模型时才返回它的副本forward_range
(这是合理的,因为预增量的迭代器会input_range
使其所有副本无效)。由于的子范围
istreambuf_iterator
只是一个input_range
,transform_view
因此,迭代器的后增量运算符不返回任何内容( ),这不符合Cpp17InputIteratorvoid
的要求,后者要求必须是格式良好的,即后增量运算符的返回类型应该是可取消引用的。*it++
因此,在您的示例中,
transform_view
的迭代器不是有效的 C++17 输入迭代器,它只是一个 C++17 输出迭代器,这使得iterator_traits
没有value_type
成员。