Последнее обновление: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"));
}
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());
Похожие темы¶
- Шесть способов разделения строк в C++ | C++那些事
- Заметки о разработке расширений CPython | C++那些事
- Памятка по разработке на C++ с libcurl | C++那些事
- Как читать открытый C++-код | C++那些事
- Интересный фрагмент C++-кода | C++那些事