for (auto ele : std::make_tuple(1, 2)) {
cout << ele << endl;
}
template <typename... Args>
class tuple {
public:
// The iterator class is templated with an integer that denotes which
// member of the tuple it must fetch from, the tuple type is included
// for forwarding reasons
template <int index, typename TupleType>
class Iterator {
public:
// this tells the range based for loop to not use the operator++()
// member function to increment the iterator, but rather to use the
// operator++(int) postfix increment method
using preferred_increment = std::post_increment_tag;
// The constructor initializes the iterator with a reference to the
// tuple
Iterator(tuple<Args...>& tup_in) : tup{tup_in} {}
// The dereference operator forwards the tuple and returns the forwarded
// result obtained on a call to std::get<>
decltype(auto) operator*() {
return std::get<index>(std::forward<TupleType>(this->tup));
}
// The iterator only defines a postfix increment, as is needed for
// heterogenosity
auto operator++(int) {
return Iterator<index + 1, TupleType>(this->tup);
}
// return a false for iterators that are not of the same type
template <int index_other, typename TupleType>
constexpr bool operator!=(const Iterator<index_other, TupleType&) {
return index_other != index;
}
};
// The begin() member function returns an iterator type that will return
// lvalue references on dereferencing
auto begin() & {
return Iterator<0, tuple<Args...>&>{*this};
}
// The begin() member function returns an iterator type that will return
// rvalue references on dereferencing
auto begin() && {
// or Iterator<0, tuple<Args...>>{*this};
return Iterator<0, tuple<Args...>&&>{*this};
}
// The end iterator returns an iterator with the sizeof(Args)... as the
// index, so any iterator type that reaches it will compare the same
// The tuple type really doesn't matter here
auto end() {
return Iterator<sizeof(Args)..., tuple<Args...>>{*this};
}
};
for (auto element : std::make_tuple(1, "some string")) {
cout << element << endl;
}
for (auto val : std::fibonacci{}) {
cout << val << endl;
}
template <typename... Args>
void foo(Args&&... args_in) {
auto args = std::forward_as_tuple(std::forward<Args>(args_in)...);
for (auto&& arg : args) {
// ...
}
}
// compile time iteration through a range of integers
for (auto index : std::make_integer_sequence<int,10>{}) {
cout << std::get<static_cast<int>(index)>(some_tuple) << endl;
}
C++17 has a new feature that would improve the functionality of range based for loops to work with begin and end iterators that are not of the same type as proposed by Eric Niebler here http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0184r0.html. This is a great feature, as it generalizes the range based for loop to fit scenarios that would otherwise not be possible, essentially making it more adaptable.
But something like the following is still not possible using the range based for loop, since the dereferenced iterators must be the same type, and therefore cannot contain any compile time information
for (auto ele : std::make_tuple(1, 2)) {
cout << ele << endl;
}
--
You received this message because you are subscribed to the Google Groups "ISO C++ Standard - Future Proposals" group.
To unsubscribe from this group and stop receiving emails from it, send an email to std-proposal...@isocpp.org.
To post to this group, send email to std-pr...@isocpp.org.
To view this discussion on the web visit https://groups.google.com/a/isocpp.org/d/msgid/std-proposals/05e0675e-4c5d-4ad4-a1e4-4cf10ef5bf96%40isocpp.org.
C++17 has a new feature that would improve the functionality of range based for loops to work with begin and end iterators that are not of the same type as proposed by Eric Niebler here http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0184r0.html. This is a great feature, as it generalizes the range based for loop to fit scenarios that would otherwise not be possible, essentially making it more adaptable.But something like the following is still not possible using the range based for loop, since the dereferenced iterators must be the same type, and therefore cannot contain any compile time information
for (auto ele : std::make_tuple(1, 2)) {
cout << ele << endl;
}
Iterating through a tuple requires use of minimal amounts of metaprogramming, it is easy to write a small helper that would automate this for us.
But then again, it requires extra work on the programmers part. A language solution seems to be more intuitive to me - generalize the range based for loop to the stage where it can accept a range of different iterator types.
template <typename Range>
void do_something_with_range(Range&& range) {
for (auto&& element : range) {
// do something with range
}
}
// with the new rules for type deduction in constructors
do_something_with_range(std::tuple{1, "something", 3.14159265});
do_something_with_range(std::vector{1, 2, 3, 4});
template <typename... Args>
void do_something_with_range(Args&&... args) {
auto t = std::forward_as_tuple(std::forward<Args>(args)...);
do_something_with_range_impl(t);
}
template <typename Vector>
void do_something_with_range(Vector&& vec) {
do_something_with_range_impl(std::forward<Vector>(vec));
}
{
auto && __range = range_expression ;
for (auto __begin = begin_expr, __end = end_expr;
__begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
{
auto && __range = range_expression ;
auto __begin = begin_expr ;
auto __end = end_expr ;
for ( ; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
auto && __range = range_expression ;
constexpr if (is_std_get_defined_for(__range)) {
// unrolled loop from 0 to
// std::tuple_size_v<std::decay_t<decltype(__range)>> follows
{
range_declaration = std::get<0>(__range);
loop_statement
}
{
range_declaration = std::get<0>(__range);
loop_statement
}
...
} constexpr else {
auto __begin = begin_expr ;
auto __end = end_expr ;
for ( ; __begin != __end; ++__begin) {
range_declaration = *__begin;
loop_statement
}
}
C++17 has a new feature that would improve the functionality of range based for loops to work with begin and end iterators that are not of the same type as proposed by Eric Niebler here http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2016/p0184r0.html. This is a great feature, as it generalizes the range based for loop to fit scenarios that would otherwise not be possible, essentially making it more adaptable.But something like the following is still not possible using the range based for loop, since the dereferenced iterators must be the same type, and therefore cannot contain any compile time information
for (auto ele : std::make_tuple(1, 2)) {
cout << ele << endl;
}
Iterating through a tuple requires use of minimal amounts of metaprogramming, it is easy to write a small helper that would automate this for us. But then again, it requires extra work on the programmers part. A language solution seems to be more intuitive to me - generalize the range based for loop to the stage where it can accept a range of different iterator types.
Concerns with this specific proposal included performance considerations (for example, it makes it very easy to write innocent-looking code that triggers a lot of template instantiations and is expensive to compile), and the fact that it didn’t generalize to some use cases (such as cases where you want the type of a result to depend on logic in your “loop”). Further exploration of the topic was encouraged.
@Nicol BolasThanks for the reply! While there are other solutions, why not improve the range based for loop even more? There was no strict need for sentinels, but that was added as well.
There were other ways to accomplish the same thing without them but adding them brings more generalizability to the construct. That's why I suggested the change.