Перейти к содержанию

Последнее обновление:2026-10-12

Печать чисел от 1 до 100 без циклов в C++

Ставим вам условие: не используя циклы, выведите числа от 1 до 100.

С циклами это совсем просто, но сколько способов без них вы сможете придумать?

Сегодня покажу несколько хитрых приёмов C++, которыми можно написать такой код; пишите комментарии и делитесь~

Эта задача на самом деле очень проста, но затрагивает множество тем с собеседований, включая:

  • специализацию и частичную специализацию шаблонов, как писать рекурсивные шаблоны
  • CRTP
  • как использовать std::copy, std::iota, std::transform и реализацию этих алгоритмов
  • статические переменные класса
  • и так далее

1. Рекурсия

1–100 можно разложить как 5*5*4 и печатать тремя функциями: функция a делает один cout и увеличивает переменную на 1; функция b вкладывает a друг в друга 5 раз; функция c вкладывает b друг в друга 5 раз; и наконец в main функция c вкладывается 4 раза.

#include <iostream>

int a(int n) {
  std::cout << n++ << std::endl;
  return n;
}

int b(int n) { return a(a(a(a(a(n))))); }

int c(int n) { return b(b(b(b(b(n))))); }

int main() {
  c(c(c(c(1))));
  return 0;
}

2. Перегрузка шаблонов

Используем шаблоны и специализацию: при итерации до 100 выводим только 100, иначе выводим I и рекурсивно обрабатываем с I+1.

#include <iostream>
template <int I>
struct JustPrint : public JustPrint<I + 1> {
  JustPrint() : JustPrint<I + 1>() { std::cout << I << " "; }
};

template <>
struct JustPrint<100> {
  JustPrint<100>() { std::cout << 1 << " "; }
};

int main() { JustPrint<1> j; }

3. CRTP

Способ 3 основан на способе 2, но переписан в стиле CRTP — уже немного непонятно, да?

Ниже struct можно заменить на class, здесь я опишу через класс. По сути это два класса: первый класс Print — родительский, печатает текущее I и вызывает дочерний класс; дочерний класс PrintDerived наследует Print и передаёт сам себя родителю; дочерний класс — это класс со специализацией, управляющий остановкой рекурсии.

#include <iostream>

template <typename Derived, int I>
struct Print {
  Print() {
    std::cout << I << " ";
    Derived::next();
  }
};

template <int I>
struct PrintDerived : public Print<PrintDerived<I>, I> {
  static void next() { PrintDerived<I + 1>(); }
};
template <>
struct PrintDerived<100> : public Print<PrintDerived<100>, 100> {
  static void next() {}
};

int main() {
  PrintDerived<1> j;
  return 0;
}

4. Статическая переменная класса

Создаём массив Test из 100 элементов; статическая переменная увеличивается при каждом создании.

#include <iostream>
using namespace std;

class Test {
  static int i;

 public:
  Test() { cout << ++i << endl; }
};
int Test::i;
int main() {
  Test a[100];
  return 0;
}

5. Использование библиотеки алгоритмов

Используем std::for_each — просто и грубо.

int main() {
  std::vector<int> l(100);
  std::iota(l.begin(), l.end(), 1);
  std::for_each(l.begin(), l.end(), [](int& n) { std::cout << n << std::endl; });
  return 0;
}

6. C++20 ranges

Используем std::views из C++20 и печатаем во время std::copy.

#include <iostream>
#include <iterator>
#include <ranges>
int main() {
  auto numbers = std::views::iota(1, 100 + 1);
  std::copy(numbers.begin(), numbers.end(), std::ostream_iterator<int>(std::cout, "\n"));
}
Этот способ можно ещё преобразовать: есть другая реализация — через std::transform.

auto range = std::views::transform(std::ranges::iota_view{1, 101}, [](const auto& x) {
  std::cout << x << std::endl;
  return x;
});
std::vector<int> a(range.begin(), range.end());

Похожие темы

Комментарии