c ++ 14 — n = {0,1, …, n-1} в переполнении стека

Формальное определение (в теории множеств) натурального числа n состоит в следующем:

  • 0 пустое множество
  • 1 = {0}
  • n = {0,1, …, n-1}

Я думаю, что это сделало бы некоторый код 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, но мне интересно, возможно ли это сделать без него.

0

Решение

Это не может быть сделано. Из раздела 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))
10

Другие решения

Если вы посмотрите на страница 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,

живое демо

1

Просто для удовольствия …

Вы пометили этот вопрос 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;
}
0

Вы можете использовать некоторый синтаксис, близкий к тому, что вы хотите, но вам понадобится массив с числами, через которые вы хотите выполнить итерацию.

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

-1
По вопросам рекламы [email protected]