c – 迭代容器或范围 – constness的问题

前端之家收集整理的这篇文章主要介绍了c – 迭代容器或范围 – constness的问题前端之家小编觉得挺不错的,现在分享给大家,也给大家做个参考。
我正在尝试编写一个模板函数,它将汇总某些集合的所有元素 – 指定为普通的stl容器,或指定为range-v3的范围. (实际的功能,如下所示更通用)我认为这样可行:
template <typename Range,typename Ret,typename Func>
std::pair<Ret,int> sum(const Range& range,Ret zero,Func extract) {
  using It = decltype(range.begin());
  Ret sum = zero;
  int numElements = 0;
  for (It it = range.begin(); it != range.end(); ++it) {
    sum += extract(*it);
    ++numElements;
  }
  return { sum,numElements };
}

这确实适用于STL元素,但不适用于范围.这给了我一个很长的错误

<this file,at line 'using It'> error C2662: 'ranges::v3::basic_iterator<ranges::v3::adaptor_cursor<ranges::v3::basic_iterator<ranges::v3::adaptor_cursor<std::_Tree_const_iterator<std::_Tree_val<std::_Tree_simple_types<_Ty>>>,ranges::v3::iter_transform_view<Rng,ranges::v3::indirected<Fun>>::adaptor<false>>>,ranges::v3::remove_if_view<ranges::v3::transform_view<Rng,Fun>,ranges::v3::logical_negate_<EnemyGroup::stepUpdate::<lambda_c582fb1297dce111c4572cef649d86b9>>>::adaptor>> ranges::v3::view_facade<Derived,ranges::v3::finite>::begin<Derived,false,0x0>(void)': cannot convert 'this' pointer from 'const Range' to 'ranges::v3::view_facade<Derived,ranges::v3::finite> &'
note: Conversion loses qualifiers

最初,我认为这是范围-v3的vs2015分支的一些缺陷.没有多想,我只是快速解决了一下:

template <typename Range,Func extract) {
  using It = decltype(const_cast<Range*>(&range)->begin());
  Ret sum = zero;
  int numElements = 0;
  for (It it = const_cast<Range*>(&range)->begin(); it != const_cast<Range*>(&range)->end(); ++it) {
    //sum += extract(std::as_const(*it)); (does not work either,converts to void)
    sum += extract(*it);
    ++numElements;
  }
  return { sum,numElements };
}

但是,从预览版中推出的最新MSVC版本,现在正式支持范围的主分支.然而,上述错误占上风.

>使用范围的对象作为const&做错了什么?我知道这些对象很轻,很容易复制,但使用const引用不应该受到伤害,或者?另一方面,如果通过一个具体的STL容器,我需要它作为const& amp;
>如果使用const&是不正确的,有一些简单的方法函数兼容容器和范围,而无需在调用站点写任何东西(例如调用view :: all)

我正在使用Visual Studio Community 2017,版本15.9.3.注意,在15.9之前,不支持其主分支中的range-v3.

既然你在问我究竟是怎么称呼它的.我的实际代码很复杂,但我把它简化为这个小例子:

#include <set>
#include <range/v3/view/filter.hpp>

template <typename Range,numElements };
}

int main() {
  std::set<int*> units;
  auto [vsum,num] = sum(
    units | ranges::v3::view::filter([](const int* eu) { return *eu>0; }),[](const int* eu) { return *eu/2; }
  );
}

这给了我与上面相同的转换错误.

解决方法

并非所有范围都是可扩展的.也就是说,存在范围类型T,其中const T不是范围. filter是典型的例子:它需要缓存从begin返回的迭代器的值,以便将来的调用是O(1)(见 http://eel.is/c++draft/range.filter.view#6).因此,begin不能成为const成员函数而不违反标准库策略,即const成员可以从多个线程调用而不引入数据争用.

因此,const Range&接受一般的Range参数并不是惯用的,因为它是接受“我不打算修改的容器”.我们建议采用Range参数的函数通过转发引用来接受它们.如果您将程序更改为:

#include <set>
#include <range/v3/view/filter.hpp>

template <typename Range,int> sum(Range&& range,Func extract) { // Note "Range&&"
  Ret sum = zero;
  int numElements = 0;
  for (auto&& e : range) {
    sum += extract(e);
    ++numElements;
  }
  return { sum,[](const int* eu) { return *eu/2; }
  );
}

它将编译并正确运行.

猜你在找的C&C++相关文章