A few minutes ago, a colleague on another team asked:
I really enjoyed your talk on Modern C++ from the Build conference, and have a quick question: Could there be a simpler syntax – something like:
foreach(collection, lambda_function) // or some other syntactic name for “foreach”
which would expand to
for_each(begin(collection), end(collection), lambda_function)
Same for find_if, etc.
This was considered and is desirable. In today’s C++ it’s easy to do for some algorithms, but not others. The main problem is overloading, which needs better template support (i.e., C++0x concepts which were proposed but didn’t make it) and/or adding enable_if.
Briefly, the basic problem is that you already have predicate overloads for some algorithms:
template<typename Iter> void sort( Iter, Iter ); // 1 template<typename Iter, typename Pred> void sort( Iter, Iter, Pred ); // 2
So far, so good. But what we want to add is:
template<typename Container> void sort( Container& ); // 3 template<typename Container, typename Pred> void sort( Container&, Pred ); // 4
And 4 is difficult to distinguish cleanly from 1 today. Both are match-anything function templates taking two parameters, and 1 would be preferred by the language when the argument types are identical, but in common cases you encounter unfortunate effects. For example, Howard Hinnant points out that if you try to call sort with (say) an iterator and a const_iterator, if we had only 1 you’d get a reasonably clear error message, but now because 4 is able to match different parameter types the compiler will instead try to invoke 4, which isn’t at all close to the original intent, and you’ll get a deeply strange template error message somewhere in the bowels of 4’s implementation because it wasn’t expecting anything like an iterator for either parameter.
Of course, not all algorithms have this issue where there are already overloads with different numbers of parameters. I’m hopeful that the standard library will get range-based overloads of all standard algorithms that are enable_if’d to avoid the problem or can use concepts if those make it into a future standard.
Here’s what the enable_if workaround might look like (I haven’t actually tried this though):
template<typename Iter> typename enable_if< !CallableWithBeginAndEnd< Iter >::value, void >::type sort( Iter, Iter ); // 1 template<typename Iter, typename Pred> void sort( Iter, Iter, Pred ); // 2 template<typename Container> void sort( Container& ); // 3 template<typename Container, typename Pred> typename enable_if< CallableWithBeginAndEnd<Container>::value, void >::type sort( Container&, Pred ); // 4