導入
C++20 では、書き味を大きく改善する機能がまとめて追加されました。テンプレートに制約をかける concepts、コンテナ操作を簡潔にする ranges、安全な文字列整形の std::format、配列を安全に受け渡す std::span を紹介します。
この回は読み物です。C++20 の機能は対応コンパイラが比較的新しいため、コードは手元の環境(対応した g++ / clang など)で確かめてください。
説明
concepts は、テンプレートの型引数に「こういう性質を満たす型だけ」という制約をかける仕組みです。第13章のテンプレートは、渡された型が想定と違うと分かりにくいエラーになりがちでしたが、concepts を使うとエラーメッセージが分かりやすくなり、意図も読み手に伝わります。
#include <iostream>
#include <concepts>
using namespace std;
template <std::integral T> // T は「整数型」に限定する制約(concept)
T doubleIt(T x) {
return x * 2;
}
int main() {
cout << doubleIt(21) << endl; // OK(int は integral)
// doubleIt(3.14); // エラー(double は integral でない)
return 0;
}
template <std::integral T> の代わりに template <typename T> requires std::integral<T> と requires 節で書くこともできます。
ranges は、コンテナに対する「フィルタ」「変換」などの操作を、|(パイプ)でつなげて書ける仕組みです。第15章のアルゴリズムと違い、必要になるまで実際には計算しない「遅延評価」が特徴です。
#include <iostream>
#include <vector>
#include <ranges>
using namespace std;
int main() {
vector<int> v = {1, 2, 3, 4, 5, 6};
auto evens = v | std::views::filter([](int x) { return x % 2 == 0; })
| std::views::transform([](int x) { return x * x; });
for (int x : evens) cout << x << " "; // 4 16 36
cout << endl;
return 0;
}
std::format は、printf に似た書式指定を、型安全に行える文字列整形です。%d のような型指定を書き間違える心配がありません。
#include <iostream>
#include <format>
using namespace std;
int main() {
string s = std::format("{} は {} 歳です", "たろう", 20);
cout << s << endl; // たろう は 20 歳です
return 0;
}
std::span は、配列や vector の「連続した領域」を、コピーせずに軽く参照できる型です。「先頭ポインタと個数」を安全な形でまとめたもの、とイメージすると分かりやすいです。生の配列でも vector でも、同じ関数で受け取れます。
#include <iostream>
#include <span>
#include <vector>
using namespace std;
void printAll(std::span<int> s) { // 配列でも vector でも受け取れる
for (int x : s) cout << x << " ";
cout << endl;
}
int main() {
int arr[] = {1, 2, 3};
vector<int> v = {4, 5, 6, 7};
printAll(arr); // 1 2 3
printAll(v); // 4 5 6 7
return 0;
}
まとめ
concepts はテンプレートに型の制約をかけて意図を明示し、ranges は | で処理をつなげる遅延評価のコンテナ操作です。std::format は型安全な文字列整形、std::span は配列/vector を統一的に安全に受け渡せるビューです。いずれも、C++20 以降の実務コードでよく見かける道具です。