簡體   English   中英

迭代容器或范圍 - constness的問題

[英]Iterating over container or range - problem with 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, 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 };
}

但是剛剛從預覽中出來的最新MSVC版本,現在正式支持范圍的主分支。 然而,上述錯誤占上風。

  • 使用范圍的對象作為const&錯誤的事情嗎? 我知道這些對象很輕,易於復制,但使用const引用不應該受到傷害,或者? 另一方面,如果傳遞一個具體的STL容器,我需要它作為const&傳遞
  • 如果使用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, 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; }
  );
}

這給了我與上面相同的轉換錯誤。

並非所有的范圍是const -iterable。 也就是說,存在范圍類型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, 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; }
  );
}

它將編譯並正確運行。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM