The Iterator Iterator (Extending Range-Based For Loops)
blog.brainstembreakfast.com/update/c++/2014/1...Posted Oct 19, 2014, 10:15 PM UTC
1114
Posted Oct 19, 2014, 10:15 PM UTC
Not my idea, but you can use boost::counting_range to do this. E.g. the following prints "0 1 2":
int main()
{
std::vector<int> v{0, 1, 2};
for(const auto it : boost::counting_range(v))
{
std::cout << *it << std::endl;
}
}
Ah, there you go. I figured someone else must have done the same thing at some point. Thanks for bringing this to my attention.
I just wish I'd thought of it myself! :)
I think that this is unnecessarily complicated and that
for (auto i = c.begin(); i != c.end(); ++i)is entirely reasonable for the now-rare cases when you need an iterator.The depicted implementation solves the nested temporaries problem (very good; many people miss this), but is unnecessarily complicated at one point:
template <typename T> auto iit(T&& t) -> typename std::conditional<std::is_lvalue_reference<decltype(t)>::value, ref<T>, val<T>>::typeHere, the trailing return type and
decltype(t)are unnecessary, because you can and should simply inspectT.Yea, it is extra work in the implementation, but usage is straight forward. I would prefer a for_it syntax though.
I wasn't sure if T would properly evaluate to an l or r value, or if I needed the type of t, so I went the safe route. I mean I didnt know if I have the universal ref of T meant T would evaluate to let's say
intorint &when passed a r-value ref. My confusion stems from t's type being T & t or T && t. What I don't get if t is T & then how could T also evaluate to T ref.Thanks for the critique!
Edit Tried to explain my confusion more.
What Scott Meyers calls "universal references" and Herb Sutter now calls "forwarding references" are powered by what I call the "template argument deduction tweak", followed by what the Standard calls "reference collapsing". (Anyone who says that it's just reference collapsing is wrong.)
Given
template <typename T> meow(T&& t),string lvalue("purr");, andstring rvalue() { return "hiss"; }, let's examine what happens for:meow(lvalue)is called. Template argument deduction notices the universal/forwarding reference pattern (T&&, whereTis a template parameter belonging to the function template itself; nothing else fits the pattern), so the tweak is activated when the argument is an lvalue.Tis deduced to bestring&(without the tweak, template argument deduction never deduces types to be references). Then, the compiler needs to substitute this deducedTintoT&&to get the function parameter's type. For a split second,string& &&is formed, but C++ doesn't permit references to references, so reference collapsing activates. The rule is that "lvalue references are infectious", so this collapses tostring&. Therefore the function parameter isstring& t.Given the call
meow(rvalue()), the argument expression is an rvalue, so the template argument deduction tweak doesn't activate (it's only for lvalues plus the perfect forwarding pattern). SoTis deduced to bestring, that's substituted intoT&&to getstring&&, and the function parameter isstring&& t.Reference collapsing also activates when you try to form references to references via typedefs; you can play around with it that way and see what happens.
Awesome explanation. I like the idea of testing it and learning through typedefs, and I can evaluate the type instantly with intellisense.
I have seen a few of your channel 9 videos and you are always really good at breaking down complex topics to be easy to understand.
Much appreciated.
Interesting, it seems that this means that in the following,
Twill be deduced differently forxandy.And indeed it is, and so it doesn't compile: http://ideone.com/yDqnqU
Note that while the tweak applies to only the perfect forwarding pattern, template argument deduction for lvalue references also differs from template argument deduction for values. Try
(T& a, T b)called withconst int c = 0;.I think the benefit to something like this is because the ranged-based for implementation hides the usual begin-end calls, whereas the the old-style for loop requires that boilerplate code.
That said, I think a better implementation would simply be a variation on the (hopefully soon to come) container based for-each:
Or something of the sort.
i havn't heard of the container proposal. Sometimes I do wish the standard algoritms work work on plain containers. I am hoping Eric Niebler's range proposals will bring some benfits to the standard algorithms.
I also wrote an iit that worked with a begin and end like:
by converting to a range object first, but didn't include it.
Can't do "break" and "continue" with those though.
That's a good point.
Good, then you'll start using
std::find_if.