【问题标题】:boost::range::join for multiple rangesboost::range::join 用于多个范围
【发布时间】:2013-01-16 19:48:05
【问题描述】:

我想做以下事情:

std::vector<int> a = {1,2,3}, b = {4,5,6}, c = {7,8,9};

for(auto&& i : join(a,b,c)) {
  i += 1
  std::cout << i;  // -> 2345678910
}

我尝试使用boost::range::join,效果很好:

auto r = boost::join(a,b);
for(auto&& i : boost::join(r,c)) {
  i += 1;
  std::cout << i;  // -> 2345678910
}

链式连接,读取操作工作:

for(auto&& i : boost::join(boost::join(a,b),c))
  std::cout << i;  // -> 123456789

但是,写作不起作用:

for(auto&& i : boost::join(boost::join(a,b),c)) {
  i += 1; // Fails  :(
  std::cout << i;  
}

我的可变参数连接也有同样的问题,即适用于阅读但不适用于写作:

template<class C> C&& join(C&& c) { return c; }

template<class C, class D, class... Args>
auto join(C&& c, D&& d, Args&&... args)
-> decltype(boost::join(boost::join(std::forward<C>(c), std::forward<D>(d)),
                     join(std::forward<Args>(args)...))) {
return boost::join(boost::join(std::forward<C>(c), std::forward<D>(d)),
                     join(std::forward<Args>(args)...));
}

Mehrdad 在 cmets 中给出了解决方案

template<class C>
auto join(C&& c)
-> decltype(boost::make_iterator_range(std::begin(c),std::end(c))) {
return boost::make_iterator_range(std::begin(c),std::end(c));
}

template<class C, class D, class... Args>
auto join(C&& c, D&& d, Args&&... args)
-> decltype(boost::join(boost::join(boost::make_iterator_range(std::begin(c),std::end(c)),
                                 boost::make_iterator_range(std::begin(d),std::end(d))),
                     join(std::forward<Args>(args)...))) {
  return boost::join(boost::join(boost::make_iterator_range(std::begin(c),std::end(c)),
                                 boost::make_iterator_range(std::begin(d),std::end(d))),
                     join(std::forward<Args>(args)...));
}

【问题讨论】:

  • 这个 可能 工作(但归功于 jrok,因为他解释了问题背后的原因):boost::join(boost::join(boost::make_iterator_range(a.begin(), a.end()), boost::make_iterator_range(b.begin(), b.end())), boost::make_iterator_range(c.begin(), c.end()))
  • @Mehrdad 谢谢,它有效!我将用解决方案更新问题。你能解释一下它为什么起作用吗? boost::make_iterator_range 也返回一个左值,所以应该选择 join 的 const ref 重载,对吧?
  • 是的。这是因为 range a容器本身,而像 vector 这样的 const 容器会将其 const 特性传播到其元素上。另一方面,iterator range 不是容器——它只是一对迭代器。并且迭代器的 const-ness 与迭代器指向的 const-ness 完全无关。另一方面,虽然我对此不确定,但我认为将 boost::make_iterator_range(a.begin(), a.end()) 替换为 boost::make_iterator_range(a) 也可能有效。
  • 仅作记录 - 如果您只使用左值,您的 join 模板也可以工作(重命名后,带有两个参数的调用与 boost::join 不明确)。
  • @jrok 您是指标记为解决方案的包装器还是上一个? (上一个有同样的初始问题)

标签: c++ boost join range


【解决方案1】:

two overloads of boost::join

template<typename SinglePassRange1, typename SinglePassRange2>
joined_range<const SinglePassRange1, const SinglePassRange2>
join(const SinglePassRange1& rng1, const SinglePassRange2& rng2)

template<typename SinglePassRange1, typename SinglePassRange2>
joined_range<SinglePassRange1, SinglePassRange2>
join(SinglePassRange1& rng1, SinglePassRange2& rng2);

当你这样做时

for(auto&& i : boost::join(boost::join(a,b), c)) {
           //  ^^^^        ^^^^ temporary here
           //   ||
           //  calls the const ref overload

你得到一个临时的joined_range,因为它们只能绑定到 const 引用,所以选择了第一个重载,它返回一个不允许修改的范围。

如果你避免临时工作,你可以解决这个问题:

#include <boost/range.hpp>
#include <boost/range/join.hpp>

int main()
{
    std::vector<int> a = {1,2,3}, b = {4,5,6}, c = {7,8,9};
    auto range = boost::join(a,b);

    for(int& i : boost::join(range,c)) {
        i += 1;
        std::cout << i;
    }
}

Live demo.

我没有调查你的可变参数函数,但问题可能类似。

【讨论】:

  • 可变参数范围也是同样的问题。 imo 最好的解决方案是为 boost.range 打一个补丁,为右值引用提供重载。
  • 谢谢!同意,当链接两个 boost::join 时,函数调用会产生一个绑定到 const 左值引用的临时值(右值引用没有 boost::join 重载)。 “我们的”解决方法有效。但是,在可变参数包装器内,递归只会产生临时变量,因此更难实现。
猜你喜欢
  • 2016-10-14
  • 2010-09-14
  • 2015-08-08
  • 1970-01-01
  • 1970-01-01
  • 2019-01-22
  • 2018-05-01
  • 1970-01-01
  • 2023-04-09
相关资源
最近更新 更多