I am trying to write a template function that will sum up all elements of some collection - specified either as a plain stl container, or as a ranges-v3's range. (The actual function, as shown below is a bit more generic) I thought this would work:
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 };
}
This indeed works for STL elements, but not for ranges. This gives me a very long error:
<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
originally, I thought it was some deficiency of the vs2015 branch of ranges-v3. Without thinking much, I just hacked a quick walkaround:
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 };
}
but with the newest MSVC version that just came out from preview, the master branch of ranges is now officially supported. Yet, the above error prevails.
const&
a wrong thing to do? I know these objects are lightweight and are easy to copy around, but using a const reference shouldn't hurt, or? On the other hand, if a concrete STL container is passed, I need it to be passed as const&
const&
is incorrect, is there some easy way to have a function work with both containers and ranges, without writing anything at the call site (e.g. invoking view::all
)I am using Visual Studio Community 2017, Version 15.9.3. Note, that before 15.9, range-v3
in its master branch was not supported.
Since you are asking how exactly I call it. My actual code is complicated, but I reduced it down to this small example:
#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; }
);
}
This gives me the same conversion errors as above.
Not all ranges are const
-iterable. That is, there are range types T
for which const T
is not a range. filter
is the classic example: it needs to cache the value of the iterator returned from begin
so that future calls are O(1) (See http://eel.is/c++draft/range.filter.view#6). Consequently, begin
cannot be a const
member function without violating the Standard Library policy that const
members are callable from multiple threads without introducing data races.
As a consequence, const Range&
isn't idiomatic for accepting general Range
arguments as it was for accepting "container that I don't intend to modify." We recommend that functions that take Range
arguments accept them by forwarding reference. If you alter your program to:
#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; }
);
}
It will compile and run correctly.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With