最終更新:2026-10-12
C++ ループなしで 1-100 を出力¶
みなさんに 1 つの条件を課します:ループを使わずに、1〜100 の数字を出力してください。
ループを使えば簡単ですが、あなたは何通り思いつきますか?
今日は C++ のちょっとした小技を使って、このようなコードを書いてみます。コメントとシェアお待ちしています〜
この問題自体は非常にシンプルですが、多くの面接の論点を含んでいます。その中には次のものがあります:
- テンプレートの特殊化・部分特殊化、再帰テンプレートの書き方
- CRTP
- std::copy、std::iota、std::transform の使い方と各アルゴリズムの実装
- クラスの静的変数
- など
1.再帰¶
1〜100 は 5*5*4 に分解でき、3 つの関数で出力します。関数 a は cout を 1 回行い、変数を 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 を出力し、+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 に置き換えられますが、ここではクラスとして説明します。実際は 2 つのクラスです。最初のクラス 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¶
C++20 の std::views を使用し、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"));
}
この方式をさらに改造すると、もう 1 つの実装があります。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());
関連トピック¶
- C++ 文字列の 6 つの分割方法 | C++那些事
- CPython 拡張開発メモ | C++那些事
- libcurl C++ 開発メモ | C++那些事
- オープンソース C++ コードの読み方 | C++那些事
- ある面白い C++ コード | C++那些事