Reputation: 79461
Related: Template Specialization for each Range Type
In C++11 the range-based for loop handles three kinds of "ranges," outlined here (link). I've quoted the relevant part below.
Syntax
for (range_declaration : range_expression) loop_statement
Explanation
The above syntax produces code similar to the following (
__range
,__begin
and__end
are for exposition only):{ auto && __range = range_expression; for (auto __begin = begin_expr, __end = end_expr; __begin != __end; ++__begin) { range_declaration = *__begin; loop_statement } }
The
range_expression
is evaluated to determine the sequence or range will be iterated over. Each element of the sequence is dereferenced, and assigned to the variable using the type and name given in therange_declaration
.The
begin_expr
andend_expr
are defined to be either:
- If
(__range)
is an array, then(__range)
and(__range + __bound)
, where__bound
is the array bound;- If
(__range)
is a class and has either a begin or end member (or both), thenbegin_expr
is__range.begin()
andend_expr
is__range.end()
;- Otherwise,
begin(__range)
andend(__range)
, which are found based on argument-dependent lookup rules withstd
as an associated namespace.
How can I write something to get the type of the iterator the range-based for loop uses, given the type of range_expression
? (Similar to how std::iterator_traits<Iterator>::value_type
gets me the type of iterator values given the type of Iterator
.)
I want to be able to write range_traits<Range>::iterator_type
given some range type Range
and have that be the type of the iterator that a range-based for loop would use.
I tried this:
template <typename Range>
struct range_traits
{
//Try to use ADL to get correct "begin" function, or std::begin by default
using std::begin;
typedef typename decltype(begin(std::declval<Range>())) iterator_type;
};
This doesn't work, however, because in the context of a using declaration in a class body, a using
declaration is for declaring base class members.
The following does work, because the using std::begin
does what I want it to (makes std::begin
the default if ADL fails).
template <typename Range>
void example(Range& range)
{
using std::begin;
auto it = begin(range); //it is of the type I want
//the expression decltype(begin(range)) would get the type I want
}
The problem is that I can't actually get the type "out of" the function body.
Any ideas as to how to accomplish this task? Is there some SFINAE magic that can be used? If it's not clear what I'm trying to accomplish, I'll try to make it clearer.
Upvotes: 1
Views: 1353
Reputation: 8972
This seem to have worked ok:
#include <iterator>
namespace range_trait_namespace {
using std::begin;
template<typename Range>struct range_traits {
typedef decltype(begin(std::declval<Range>())) iterator_type;
};
}
#include <iostream>
#include <vector>
int main(int, char**) {
std::cout << "oi\n";
std::vector<int> i { 1, 2, 3, };
for( auto x: i )
std::cout << x << "\n";
for( range_trait_namespace::range_traits<std::vector<int>>::iterator_type aa = i.begin(); aa != i.end(); ++aa)
std::cout << *aa << "\n";
return 0;
}
Upvotes: 2