Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/130.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 如何编写使用临时容器的范围管道?_C++_Range V3 - Fatal编程技术网

C++ 如何编写使用临时容器的范围管道?

C++ 如何编写使用临时容器的范围管道?,c++,range-v3,C++,Range V3,我有一个具有此签名的第三方功能: std::vector<T> f(T t); 但是,这不起作用,因为我们无法创建临时容器的视图 range-v3是如何支持这样的范围管道的?我怀疑它就是不能。没有一个视图有任何机制可以在任何地方存储临时数据-这明显违背了以下视图的概念: 视图是一种轻量级包装器,它以某种自定义方式呈现底层元素序列的视图,而无需对其进行修改或复制。视图的创建和复制成本很低,并且具有非拥有的引用语义 因此,为了使join起作用并超过表达式的寿命,必须在某个地方保留这些临

我有一个具有此签名的第三方功能:

std::vector<T> f(T t);
但是,这不起作用,因为我们无法创建临时容器的视图


range-v3是如何支持这样的范围管道的?

我怀疑它就是不能。没有一个
视图
有任何机制可以在任何地方存储临时数据-这明显违背了以下视图的概念:

视图是一种轻量级包装器,它以某种自定义方式呈现底层元素序列的视图,而无需对其进行修改或复制。视图的创建和复制成本很低,并且具有非拥有的引用语义

因此,为了使
join
起作用并超过表达式的寿命,必须在某个地方保留这些临时变量。这可能是一个
动作
。这将起作用():

除了显然不是因为
src
是无限的,而且即使是有限的
src
也可能会增加太多的开销,使您无论如何都不想使用


您可能需要复制/重写
view::join
,以使用
view::all
(必需)的一些经过巧妙修改的版本,该版本不需要左值容器(并在其中返回迭代器对),而是允许在内部存储右值容器(并将迭代器对返回到存储的版本中)。但这需要复制几百行代码,因此即使这样做有效,似乎也不太令人满意。

编辑

显然,下面的代码违反了视图不能拥有其引用的数据的规则(但是,我不知道是否严格禁止编写这样的代码)

我使用
ranges::view\u facade
创建一个自定义视图。它保存一个由
f
返回的向量(一次一个),将其更改为一个范围。这使得可以在这样的范围内使用
view::join
。当然,我们不能对元素进行随机或双向访问(但是,
view::join
本身会将一个范围降级为一个输入范围),我们也不能分配给它们

我从Eric Niebler那里复制了
struct MyRange
,并对其进行了轻微修改

#include <iostream>
#include <range/v3/all.hpp>

using namespace ranges;

std::vector<int> f(int i) {
    return std::vector<int>(static_cast<size_t>(i), i);
}

template<typename T>
struct MyRange: ranges::view_facade<MyRange<T>> {
private:
    friend struct ranges::range_access;
    std::vector<T> data;
    struct cursor {
    private:
        typename std::vector<T>::const_iterator iter;
    public:
        cursor() = default;
        cursor(typename std::vector<T>::const_iterator it) : iter(it) {}
        T const & get() const { return *iter; }
        bool equal(cursor const &that) const { return iter == that.iter; }
        void next() { ++iter; }
        // Don't need those for an InputRange:
        // void prev() { --iter; }
        // std::ptrdiff_t distance_to(cursor const &that) const { return that.iter - iter; }
        // void advance(std::ptrdiff_t n) { iter += n; }
    };
    cursor begin_cursor() const { return {data.begin()}; }
    cursor   end_cursor() const { return {data.end()}; }
public:
    MyRange() = default;
    explicit MyRange(const std::vector<T>& v) : data(v) {}
    explicit MyRange(std::vector<T>&& v) noexcept : data (std::move(v)) {}
};

template <typename T>
MyRange<T> to_MyRange(std::vector<T> && v) {
    return MyRange<T>(std::forward<std::vector<T>>(v));
}


int main() {
    auto src = view::ints(1);        // infinite list

    auto rng = src | view::transform(f) | view::transform(to_MyRange<int>) | view::join;

    for_each(rng | view::take(42), [](int i) {
        std::cout << i << ' ';
    });
}

// Output:
// 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 6 6 6 6 6 6 7 7 7 7 7 7 7 8 8 8 8 8 8 8 8 9 9 9 9 9 9 
#包括
#包括
使用名称空间范围;
标准::向量f(int i){
返回std::vector(静态_cast(i),i);
}
模板
struct MyRange:ranges::view\u facade{
私人:
friend struct ranges::range\u access;
std::矢量数据;
结构游标{
私人:
typename std::vector::const_迭代器iter;
公众:
游标()=默认值;
游标(typename std::vector::const_iterator it):iter(it){}
T const&get()const{return*iter;}
bool equal(cursor const&that)const{return iter==that.iter;}
void next(){++iter;}
//输入范围不需要这些:
//void prev(){--iter;}
//std::ptrdiff_t distance_to(cursor const&that)const{return that.iter-iter;}
//无效前进(std::ptrdiff_t n){iter+=n;}
};
游标开始\游标()常量{return{data.begin()};}
游标结束\u游标()常量{return{data.end()};}
公众:
MyRange()=默认值;
显式MyRange(const std::vector&v):数据(v){}
显式MyRange(std::vector&&v)noexcept:data(std::move(v)){}
};
模板
MyRange到_MyRange(std::vector&&v){
返回MyRange(std::forward(v));
}
int main(){
auto src=view::ints(1);//无限列表
auto rng=src | view::transform(f)| view::transform(to_MyRange)| view::join;
每个(rng |视图::取(42),[](int i){

std::cout这里的问题当然是一个视图的整体概念——一个非存储的分层延迟求值器。为了跟上这个约定,视图必须传递对范围元素的引用,通常它们可以处理右值和左值引用

不幸的是,在这种特定情况下,
view::transform
只能提供一个右值引用,因为函数
f(T)
按值返回一个容器,
view::join
在尝试将视图(
view::all
)绑定到内部容器时需要一个左值

可能的解决方案都会在管道的某处引入某种临时存储。以下是我提出的选项:

  • 创建一个版本的
    view::all
    ,该版本可以在内部存储通过右值引用传递的容器(如Barry所建议的) “非存储视图”的概念,还需要一些痛苦的模板 所以我建议不要使用这个选项
  • view::transform
    步骤之后,为整个中间状态使用临时容器。可以手动完成:

    auto rng1 = src | view::transform(f)
    vector<vector<T>> temp = rng1;
    auto rng = temp | view::join;
    
    然后将
    f_-store
    传递到
    view::transform
    。由于
    f_-store
    返回左值引用,
    view::join
    现在不会抱怨

    当然,这有点像黑客,只有将整个范围简化到某个接收器(如输出容器)中才能起作用。我相信它可以经受一些简单的转换,如
    view::replace
    或更多
    view::transform
    s,但任何更复杂的东西都可以尝试访问这个
    temp
    stora通用电气以非直截了当的顺序

    在这种情况下,可以使用其他类型的存储,例如,
    std::map
    将修复该问题,并且仍然允许无限
    src
    和延迟计算,但会牺牲一些内存:

    const std::vector<T>& fc(const T& t)
    {
        static std::map<T, vector<T>> smap;
        smap[t] = f(t);
        return smap[t];
    }
    
    const std::vector&fc(const T&T)
    {
    静态std::map smap;
    smap[t]=f(t);
    返回smap[t];
    }
    
    如果你的
    f
    函数是无状态的,这个
    std::map
    也可以用来潜在地保存一些调用。如果有一种方法可以保证不再需要某个元素并将其从
    std::map
    中删除以节省内存,那么这种方法可能会得到进一步改进。然而,这取决于
    auto rng1 = src | view::transform(f)
    vector<vector<T>> temp = rng1;
    auto rng = temp | view::join;
    
    const std::vector<T>& f_store(const T& t)
    {
      static std::vector<T> temp;
      temp = f(t);
      return temp;
    }
    
    const std::vector<T>& fc(const T& t)
    {
        static std::map<T, vector<T>> smap;
        smap[t] = f(t);
        return smap[t];
    }
    
    auto rng = src | view::transform(f) | view::join;
    
    #include <iostream>
    #include <vector>
    #include <range/v3/range_for.hpp>
    #include <range/v3/utility/functional.hpp>
    #include <range/v3/view/iota.hpp>
    #include <range/v3/view/join.hpp>
    #include <range/v3/view/transform.hpp>
    
    using T = int;
    
    std::vector<T> f(T t) { return std::vector<T>(2, t); }
    
    int main() {
        std::vector<T> buffer;
        auto store = [&buffer](std::vector<T> data) -> std::vector<T>& {
            return buffer = std::move(data);
        };
    
        auto rng = ranges::view::ints
            | ranges::view::transform(ranges::compose(store, f))
            | ranges::view::join;
    
        unsigned count = 0;
        RANGES_FOR(auto&& i, rng) {
            if (count) std::cout << ' ';
            else std::cout << '\n';
            count = (count + 1) % 8;
            std::cout << i << ',';
        }
    }
    
    #include <range/v3/core.hpp>
    #include <range/v3/view/iota.hpp>
    #include <range/v3/view/transform.hpp>
    #include <range/v3/view/join.hpp>
    #include <vector>
    #include <iostream>
    #include <memory>
    
    std::vector<int> f(int i) {
        return std::vector<int>(3u, i);
    }
    
    template <class Container>
    struct shared_view : ranges::view_interface<shared_view<Container>> {
    private:
        std::shared_ptr<Container const> ptr_;
    public:
        shared_view() = default;
        explicit shared_view(Container &&c)
        : ptr_(std::make_shared<Container const>(std::move(c)))
        {}
        ranges::range_iterator_t<Container const> begin() const {
            return ranges::begin(*ptr_);
        }
        ranges::range_iterator_t<Container const> end() const {
            return ranges::end(*ptr_);
        }
    };
    
    struct make_shared_view_fn {
        template <class Container,
            CONCEPT_REQUIRES_(ranges::BoundedRange<Container>())>
        shared_view<std::decay_t<Container>> operator()(Container &&c) const {
            return shared_view<std::decay_t<Container>>{std::forward<Container>(c)};
        }
    };
    
    constexpr make_shared_view_fn make_shared_view{};
    
    int main() {
        using namespace ranges;
        auto rng = view::ints | view::transform(compose(make_shared_view, f)) | view::join;
        RANGES_FOR( int i, rng ) {
            std::cout << i << '\n';
        }
    }
    
    auto rng = views::iota(0,4)
            | views::transform([](int i) {return std::string(i, char('a'+i));})
            | views::cache1
            | views::join('-');
    check_equal(rng, {'-','b','-','c','c','-','d','d','d'});
    CPP_assert(input_range<decltype(rng)>);
    CPP_assert(!range<const decltype(rng)>);
    CPP_assert(!forward_range<decltype(rng)>);
    CPP_assert(!common_range<decltype(rng)>);
    
    auto rng = src | views::transform(f) | views::cache1 | views::join;