问题
The formal definition (in set theory) of a natural number n is as follows:
- 0 is the empty set
- 1 = {0}
- n = {0,1,...,n-1}
I think this would make some C++ code much simpler, if I was allowed to do this:
for (int n : 10)
cout << n << endl;
and it printed numbers from 0 to 9.
So I tried doing the following, which doesn't compile:
#include <iostream>
#include <boost/iterator/counting_iterator.hpp>
boost::counting_iterator<int> begin(int t)
{
return boost::counting_iterator<int>(0);
}
boost::counting_iterator<int> end(int t)
{
return boost::counting_iterator<int>(t);
}
int main()
{
for (int t : 10)
std::cout << t << std::endl;
return 0;
}
Any suggestions on how to achieve this? I get the following error with clang++:
main.cpp:22:20: error: invalid range expression of type 'int'; no viable 'begin' function available
for (int t : 10)
^ ~~
but I think I should be allowed to do this! :)
Edit: I know I can "fake" it if I add the word "range" (or some other word) in the for loop, but I'm wondering if it's possible to do it without.
回答1:
It can't be done. From section 6.5.4 of the draft of the C++ 14 standard (but C++11 will be very similar)
begin-expr and end-expr are determined as follows:
(1.1) — if _
RangeT
is an array type, [...];
Well, this one obviously doesn't apply. An int
isn't an array
(1.2) — if _RangeT is a class type, [...]
Nope, this doesn't apply either.
(1.3) — otherwise, begin-expr and end-expr are
begin(__range)
andend(__range)
, respectively,
Oo! This looks hopeful. You may need to move begin
and end
into the global namespace, but still...
where
begin
andend
are looked up in the associated namespaces (3.4.2). [ Note: Ordinary unqualified lookup (3.4.1) is not performed. — end note ]
(emphasis mine). Bother! There aren't any namespaces associated with int
. Specifically, from section 3.4.2
— If T [
int
in our case] is a fundamental type, its associated sets of namespaces and classes are both empty.
The only workround is to write a class range
which has a suitable begin and end method. Then you could write the very pythonic:
for (int i : range(5))
回答2:
If you look at the cppreference page for range-based for loops, or better yet the relevant section of the standard ([stmt.ranged]p1), you see how it determines the begin_expr
to use for the loop. The relevant one for int
is
(1.3) otherwise, begin-expr and end-expr are
begin(__range)
andend(__range)
, respectively, where begin and end are looked up in the associated namespaces ([basic.lookup.argdep]). [ Note: Ordinary unqualified lookup ([basic.lookup.unqual]) is not performed. — end note ]
(emphasis added)
Unfortunately for use case, for fundamental types such as int
, argument-dependent lookup never returns anything.
Instead, what you can do is declare a class to act as the range expression, and give it begin
and end
methods:
struct Range {
using value_type = unsigned int;
using iterator = boost::counting_iterator<value_type>;
unsigned int max;
iterator begin() const
{
return iterator(0);
}
iterator end() const
{
return iterator(max);
}
};
Potential improvements to this class include:
- Making the
begin
andend
methodsconstexpr
(this requires writing your own version ofboost::counting_iterator
, or getting Boost to make their versionconstexpr
) - Adding a user-defined literal option like
Range operator""_range
- Making it work for types other than just
unsigned int
.
live demo
回答3:
Just for fun...
You have tagged this question C++14 so you can use std::integer_sequence
and std::make_integer_sequence
.
If the natural number is known compile time (as 10
in your example), you can write a simple constexpr
function getArray()
(with helper function getArrayH
) the get an std::array
of values from zero to N-1
template <typename T, T ... Vals>
constexpr std::array<T, sizeof...(Vals)>
getArrayH (std::integer_sequence<T, Vals...> const &)
{ return { { Vals... } }; }
template <typename T, T Val>
constexpr auto getArray ()
{ return getArrayH(std::make_integer_sequence<T, Val>{}); }
and call it
for ( auto const & i : getArray<int, 10>() )
The following is a full working example
#include <array>
#include <utility>
#include <iostream>
template <typename T, T ... Vals>
constexpr std::array<T, sizeof...(Vals)>
getArrayH (std::integer_sequence<T, Vals...> const &)
{ return { { Vals... } }; }
template <typename T, T Val>
constexpr auto getArray ()
{ return getArrayH(std::make_integer_sequence<T, Val>{}); }
int main ()
{
for ( auto const & i : getArray<int, 10>() )
std::cout << i << std::endl;
}
回答4:
You may use some syntax close to what you want, but you would need an array with the numbers through which you want to iterate.
int a[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
for (int n : a)
std::cout << n << std::endl;
http://en.cppreference.com/w/cpp/language/range-for
Edit: to create the array without declaring each value you may check this question: Is there a compact equivalent to Python range() in C++/STL
来源:https://stackoverflow.com/questions/47020276/n-0-1-n-1-in-c