范围修剪视图实现不适用于反向视图
Range trim view implementation does not work with reverse view
我写了一个名为trim
的 C++20 范围视图(不是 range-v3 视图(,给定一个范围和一个一元谓词,返回一个新范围,没有满足谓词的前后元素。(range-v3 库有这样的视图,但在 C++20 中缺少它。
这是实现(这可能不是最佳的,但是关于范围库的文档并不多,所以鉴于资源有限,这是我能想到的(:
namespace rg = std::ranges;
// -------- trim ----------
template<rg::input_range R, typename P> requires rg::view<R>
class trim_view : public rg::view_interface<trim_view<R, P>>
{
private:
R base_ {};
P pred_;
mutable rg::iterator_t<R> iter_ {std::begin(base_)};
mutable rg::iterator_t<R> end_ {std::end(base_)};
public:
trim_view() = default;
constexpr trim_view(R base, P pred)
: base_(std::move(base)), pred_(std::move(pred)), iter_(std::begin(base_)), end_(std::end(base_))
{}
constexpr R base() const &
{return base_;}
constexpr R base() &&
{return std::move(base_);}
constexpr auto begin()
{
while(iter_ != std::end(base_) && pred_(*iter_)) {iter_ = std::next(iter_);}
while(end_ != iter_ && pred_(*std::prev(end_))) {end_ = std::prev(end_);}
return iter_;
}
constexpr auto begin() const requires rg::range<const R>
{
while(iter_ != std::end(base_) && pred_(*iter_)) {iter_ = std::next(iter_);}
while(end_ != iter_ && pred_(*std::prev(end_))) {end_ = std::prev(end_);}
return iter_;
}
constexpr auto begin() requires rg::random_access_range<R> && rg::sized_range<R>
{
while(iter_ != std::end(base_) && pred_(*iter_)) {iter_ = std::next(iter_);}
while(end_ != iter_ && pred_(*std::prev(end_))) {end_ = std::prev(end_);}
return iter_;
}
constexpr auto begin() const requires rg::random_access_range<const R> && rg::sized_range<const R>
{
while(iter_ != std::end(base_) && pred_(*iter_)) {iter_ = std::next(iter_);}
while(end_ != iter_ && pred_(*std::prev(end_))) {end_ = std::prev(end_);}
return iter_;
}
constexpr auto end()
{ return end_ ; }
constexpr auto end() const requires rg::range<const R>
{ return end_ ; }
constexpr auto end() requires rg::random_access_range<R> && rg::sized_range<R>
{ return end_ ; }
constexpr auto end() const requires rg::random_access_range<const R> && rg::sized_range<const R>
{ return end_ ; }
constexpr auto size() requires rg::sized_range<R>
{ return std::distance(iter_, end_); }
constexpr auto size() const requires rg::sized_range<const R>
{ return std::distance(iter_, end_); }
};
template<class R, typename P>
trim_view(R&& base, P pred)
-> trim_view<rg::views::all_t<R>, P>;
namespace details
{
template <typename P>
struct trim_view_range_adaptor_closure
{
P pred_;
constexpr trim_view_range_adaptor_closure(P pred)
: pred_(pred)
{}
template <rg::viewable_range R>
constexpr auto operator()(R && r) const
{
return trim_view(std::forward<R>(r), pred_);
}
} ;
struct trim_view_range_adaptor
{
template<rg::viewable_range R, typename P>
constexpr auto operator () (R && r, P pred)
{
return trim_view( std::forward<R>(r), pred ) ;
}
template <typename P>
constexpr auto operator () (P pred)
{
return trim_view_range_adaptor_closure(pred);
}
};
template <rg::viewable_range R, typename P>
constexpr auto operator | (R&& r, trim_view_range_adaptor_closure<P> const & a)
{
return a(std::forward<R>(r)) ;
}
}
namespace views
{
inline static details::trim_view_range_adaptor trim;
}
它工作正常。我写了一些测试来确保它没问题。
template <typename P>
void are_equal(std::vector<int> const & input, std::vector<int> const & output, P&& pred)
{
std::size_t index = 0;
for(auto i : input | views::trim(std::forward<P>(pred)))
{
assert(i == output[index]);
index++;
}
assert(index == output.size());
}
int main()
{
auto is_odd = [](const int x){return x%2==1;};
are_equal({}, {}, is_odd);
are_equal({1}, {}, is_odd);
are_equal({1,3,5}, {}, is_odd);
are_equal({2}, {2}, is_odd);
are_equal({2,4}, {2,4}, is_odd);
are_equal({2,3,4}, {2,3,4}, is_odd);
are_equal({1,2,3,4}, {2,3,4}, is_odd);
are_equal({1,1,2,3,4}, {2,3,4}, is_odd);
are_equal({2,3,4,5}, {2,3,4}, is_odd);
are_equal({2,3,4,5,5}, {2,3,4}, is_odd);
are_equal({1,2,3,4,5}, {2,3,4}, is_odd);
are_equal({1,1,2,3,4,5,5}, {2,3,4}, is_odd);
}
问题是,当我在修剪后应用views::reverse
视图时,它不再正常工作。
template <typename P>
void are_equal_reverse2(std::vector<int> const & input, std::vector<int> const & output, P&& pred)
{
std::size_t index = 0;
for(auto i : input | views::trim(std::forward<P>(pred)) | rg::views::reverse)
{
assert(i == output[index]);
index++;
}
assert(index == output.size());
}
int main()
{
auto is_odd = [](const int x){return x%2==1;};
// OK
are_equal_reverse2({}, {}, is_odd);
are_equal_reverse2({1}, {}, is_odd);
are_equal_reverse2({1,3,5}, {}, is_odd);
are_equal_reverse2({2}, {2}, is_odd);
are_equal_reverse2({2,4}, {4,2}, is_odd);
are_equal_reverse2({2,3,4}, {4,3,2}, is_odd);
are_equal_reverse2({1,2,3,4}, {4,3,2}, is_odd);
are_equal_reverse2({1,1,2,3,4}, {4,3,2}, is_odd);
// fail
are_equal_reverse2({2,3,4,5}, {4,3,2}, is_odd);
are_equal_reverse2({2,3,4,5,5}, {4,3,2}, is_odd);
are_equal_reverse2({1,2,3,4,5}, {4,3,2}, is_odd);
are_equal_reverse2({1,1,2,3,4,5,5}, {4,3,2}, is_odd);
}
范围 {2,3,4,5}变为 {2,3,4}。反向应用后,它应变为 {4,3,2}。但是,结果实际上是 {5,4,3,2}。
我希望views::reverse
std::make_reverse_iterator()
应用于修剪视图的开始和结束迭代器。这应执行以下转换:
trim_view reverse_view (expected) reverse_view (actual)
--------------------------------------------------------------------
2 3 4 5 _ _ 2 3 4 5 _ 2 3 4 5
^ ^ ^ ^ ^ ^
| | => | | | |
| end_ rend | rend |
iter_ rbegin rbegin
我不确定我在这里错过了什么。任何帮助,不胜感激。
以下是工作示例的链接:https://wandbox.org/permlink/4iFNsqiz9Y9Bfm64
首先,让我们从这个开始:
constexpr auto begin()
{
while(iter_ != std::end(base_) && pred_(*iter_)) {iter_ = std::next(iter_);}
while(end_ != iter_ && pred_(*std::prev(end_))) {end_ = std::prev(end_);}
return iter_;
}
constexpr auto begin() const requires rg::range<const R>
{
while(iter_ != std::end(base_) && pred_(*iter_)) {iter_ = std::next(iter_);}
while(end_ != iter_ && pred_(*std::prev(end_))) {end_ = std::prev(end_);}
return iter_;
}
constexpr auto begin() requires rg::random_access_range<R> && rg::sized_range<R>
{
while(iter_ != std::end(base_) && pred_(*iter_)) {iter_ = std::next(iter_);}
while(end_ != iter_ && pred_(*std::prev(end_))) {end_ = std::prev(end_);}
return iter_;
}
constexpr auto begin() const requires rg::random_access_range<const R> && rg::sized_range<const R>
{
while(iter_ != std::end(base_) && pred_(*iter_)) {iter_ = std::next(iter_);}
while(end_ != iter_ && pred_(*std::prev(end_))) {end_ = std::prev(end_);}
return iter_;
}
constexpr auto end()
{ return end_ ; }
constexpr auto end() const requires rg::range<const R>
{ return end_ ; }
constexpr auto end() requires rg::random_access_range<R> && rg::sized_range<R>
{ return end_ ; }
constexpr auto end() const requires rg::random_access_range<const R> && rg::sized_range<const R>
{ return end_ ; }
所有这些重载都做完全相同的事情,所以让我们减少到两个:
constexpr auto begin() const
{
while(iter_ != std::end(base_) && pred_(*iter_)) {iter_ = std::next(iter_);}
while(end_ != iter_ && pred_(*std::prev(end_))) {end_ = std::prev(end_);}
return iter_;
}
constexpr auto end() const
{ return end_ ; }
好吧。这是怎么回事?begin()
将调整要修剪的iter_
和end_
,end()
只是返回end_
。
这很好,如果你这样做:
auto trimmed = some_range | trim(some_pred);
auto b = trimmed.begin();
auto e = trimmed.end();
但是,如果您这样做会发生什么:
auto e = trimmed.end();
auto b = trimmed.begin();
end
在这种情况下将是some_range.end()
,它不会是这个范围的正确结束迭代器!您需要确保begin()
和end()
之间没有任何排序依赖关系 - 它们始终必须返回正确的值。
此外,trim(p)
可以全部减少为:
template <rg::viewable_range R, typename P>
constexpr auto operator ()(R && r, P pred)
{
auto negated = std::not_fn(pred);
auto f = rg::find_if(r, negated);
auto l = rg::find_if(r | std::views::reverse, negated).base();
return rg::subrange(f, l);
}
相关文章:
- FLTK 2.0构建和演示,适用于VS2019的2011年左右的代码库
- C++17 - 使用自定义分配器的节点提取/重新插入 - 适用于 clang++/libc++,但不适用于 libstd
- "string.h"在构建适用于iOS的qt应用程序中找不到消息
- 适用于 WebView2 旧版本的示例应用程序
- 在 NVIDIA GEFORCE GTX 1050 上下载适用于 Windows 10 的 openCL 1.2
- __attribute__(优化(0))) 是否适用于"recursively"?
- 为什么 std::erase(std::erase_if) 不是适用于<algorithm>任何容器的模板?
- 使用一个参数的模板函数时出错(适用于 2)
- 使用 适用于 Android 和 iOS 的 tf-lite C++ API
- 为什么这适用于 G++ 而不是 CLANG?
- 适用于 macOS 的 Xcode 应用程序。这就是我设置从USB麦克风输入获取音频的方式。一年前工作,现在没有了。为什么
- 适用于 Linux 的 c++ 上的代理脚本
- 为什么我的 SFINAE 表达式不再适用于 GCC 8.2?
- 使输出流式处理运算符适用于 boost::variant<std::vector<int>、int、double 的正确方法是什么>
- 有没有适用于Windows.lib文件的GNU二进制文件描述符(BFD)
- 模板函数仅适用于VS
- 如何在cmake中包含适用于g++或viceversa的库
- 适用于win32、linux、mac的POSIX C包装器
- WinDBG适用于从Visual Studio 2015保存的转储,但不适用于任务管理器。显示异常代码"not found"
- 从uint8_t到NPY_UINT16 PyArray_SimpleNewFromData.适用于Linux,但不适用于