代码之家  ›  专栏  ›  技术社区  ›  CygnusX1 Stack Overflow is garbage

在容器或范围上迭代-常数问题

  •  7
  • CygnusX1 Stack Overflow is garbage  · 技术社区  · 7 年前

    我正在尝试编写一个模板函数,该函数将汇总某个集合的所有元素—指定为普通stl容器或ranges-v3的range。(实际功能,如下所示更为通用)我认为这会起作用:

    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
    

    template <typename Range, typename Ret, typename Func>
    std::pair<Ret, int> sum(const Range& range, Ret zero, 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 };
    }
    

    • 正在使用范围的对象作为 const& 做错事?我知道这些对象是轻量级的,很容易复制,但是使用const引用应该不会有什么坏处,或者?另一方面,如果传递了一个具体的STL容器,则需要将其作为
    • 如果使用 康斯特; view::all )

    我正在使用Visual Studio Community 2017,版本15.9.3。注意,在15.9之前, range-v3


    既然你问我怎么称呼它。我的实际代码很复杂,但我将其简化为以下小示例:

    #include <set>
    #include <range/v3/view/filter.hpp>
    
    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 };
    }
    
    int main() {
      std::set<int*> units;
      auto [vsum, num] = sum(
        units | ranges::v3::view::filter([](const int* eu) { return *eu>0; }),
        0,
        [](const int* eu) { return *eu/2; }
      );
    }
    

    1 回复  |  直到 7 年前
        1
  •  5
  •   Casey    7 年前

    并非所有范围都是相同的 const T const T 不是一个范围。 filter 是一个经典示例:它需要缓存从 begin 因此,未来调用为O(1)(参见 http://eel.is/c++draft/range.filter.view#6 ).因此, 常数 不违反标准库策略的成员函数 常数

    因此,, const Range& 这不是接受将军的惯用说法吗 Range 范围 参数通过转发引用来接受它们。如果您将程序更改为:

    #include <set>
    #include <range/v3/view/filter.hpp>
    
    template <typename Range, typename Ret, typename Func>
    std::pair<Ret, int> sum(Range&& range, Ret zero, Func extract) { // Note "Range&&"
      Ret sum = zero;
      int numElements = 0;
      for (auto&& e : range) {
        sum += extract(e);
        ++numElements;
      }
      return { sum, numElements };
    }
    
    int main() {
      std::set<int*> units;
      auto [vsum, num] = sum(
        units | ranges::v3::view::filter([](const int* eu) { return *eu>0; }),
        0,
        [](const int* eu) { return *eu/2; }
      );
    }