Формальное определение (в теории множеств) натурального числа n состоит в следующем:
Я думаю, что это сделало бы некоторый код C ++ намного проще, если бы мне было позволено сделать это:
for (int n : 10)
cout << n << endl;
и это напечатало числа от 0 до 9.
Поэтому я попытался сделать следующее, которое не компилируется:
#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;
}
Любые предложения о том, как этого добиться? Я получаю следующую ошибку с Clang ++:
main.cpp:22:20: error: invalid range expression of type 'int'; no viable 'begin' function available
for (int t : 10)
^ ~~
но я думаю, что мне следует позволить это сделать! 🙂
редактироватьЯ знаю, что могу «подделать» его, если добавлю слово «диапазон» (или другое слово) в цикл for, но мне интересно, возможно ли это сделать без него.
Это не может быть сделано. Из раздела 6.5.4 проект стандарта C ++ 14 (но C ++ 11 будет очень похожим)
начать-выр а также конец выраж определяются следующим образом:
(1.1) — если _
RangeT
тип массива, […];
Ну, этот, очевидно, не относится. int
не массив
(1.2) — если _RangeT является типом класса, […]
Нет, это тоже не относится.
(1.3) — в противном случае, начать-выр а также конец выраж являются
begin(__range)
а такжеend(__range)
соответственно
Оо! Это выглядит обнадеживающим. Вам может понадобиться переехать begin
а также end
в глобальное пространство имен, но все же …
где
begin
а такжеend
ищутся в связанных пространствах имен (3.4.2). [Примечание: обычный неквалифицированный поиск (3.4.1)
не выполняется. — конец примечания]
(акцент мой). Беспокоить! Нет никаких пространств имен, связанных с int
, В частности, из раздела 3.4.2
— Если т [
int
в нашем случае] является фундаментальным типом, связанные с ним наборы пространств имен и классов являются пустыми.
Единственное решение — написать класс range
который имеет подходящий метод начала и конца. Тогда вы могли бы написать очень питонскую:
for (int i : range(5))
Если вы посмотрите на страница cppreference для диапазонов for
петли, или еще лучше соответствующий раздел стандарта ([Stmt.ranged] p1), вы видите, как это определяет begin_expr
использовать для цикла. Соответствующий для int
является
(1.3) иначе, начать-выр а также конец выраж являются
begin(__range)
а такжеend(__range)
соответственно, где начало и конец ищутся в связанных пространствах имен ([Basic.lookup.argdep]). [ Заметка: Обычный неквалифицированный поиск ([Basic.lookup.unqual]) не выполняется. — конечная нота ]
(акцент добавлено)
К сожалению, для варианта использования, для основных типов, таких как int
зависимый от аргумента поиск никогда ничего не возвращает.
Вместо этого вы можете объявить класс, который будет действовать как выражение диапазона, и дать ему begin
а также end
методы:
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);
}
};
Потенциальные улучшения этого класса включают в себя:
begin
а также end
методы constexpr
(это требует написания вашей собственной версии boost::counting_iterator
или заставив Boost сделать свою версию constexpr
)Range operator""_range
unsigned int
,Просто для удовольствия …
Вы пометили этот вопрос C ++ 14, чтобы вы могли использовать std::integer_sequence
а также std::make_integer_sequence
,
Если натуральное число известно время компиляции (как 10
в вашем примере), вы можете написать простой constexpr
функция getArray()
(с вспомогательной функцией getArrayH
) получить std::array
значений от нуля до 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>{}); }
и назовите это
for ( auto const & i : getArray<int, 10>() )
Ниже приводится полный рабочий пример
#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;
}
Вы можете использовать некоторый синтаксис, близкий к тому, что вы хотите, но вам понадобится массив с числами, через которые вы хотите выполнить итерацию.
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
редактировать: чтобы создать массив без объявления каждого значения, вы можете проверить этот вопрос: Есть ли компактный эквивалент Python range () в C ++ / STL