We present programming techniques to illustrate the facilities and principles of C++ generic programming using concepts. Concepts are C++'s way to express constraints on generic code. As an initial example, we provide a simple type system that eliminates narrowing conversions and provides range checking without unnecessary notational or run-time overheads. Concepts are used throughout to provide user-defined extensions to the type system. The aim is to show their utility and the fundamental ideas behind them, rather than to provide a detailed or complete explanation of C++'s language support for generic programming or the extensive support provided by the standard library. Generic programming is an integral part of C++, rather than an isolated sub-language. In particular, key facilities support general programming as well as generic programming (e.g., uniform notation for types, lambdas, variadic templates, and C++26 static reflection). Finally, we give design rationales and origins for key parts of the concept design, including use patterns, the relationship to Object-Oriented Programming, value arguments, notation, concept type-matching, and definition checking.
論文ID : 2510.08969タイトル : Concept-Based Generic Programming in C++著者 : Bjarne Stroustrup (Columbia University)分類 : cs.PL cs.SE発表時期 : 2025年論文リンク : https://arxiv.org/abs/2510.08969 本論文は、コンセプト(concepts)を用いてC++のジェネリックプログラミング機能と原理を説明するプログラミング技法を提示する。コンセプトは、C++がジェネリックコードの制約を表現するための方法である。初期の例として、本論文は、狭小化変換を排除し、不要なシンボルや実行時オーバーヘッドなしに範囲チェックを提供する単純な型システムを提供する。コンセプトは、ユーザー定義型システム拡張を提供するために広く使用されている。本論文は、C++のジェネリックプログラミング言語サポートまたは標準ライブラリの広範なサポートの詳細または完全な説明を提供するのではなく、コンセプトの実用性と背後にある基本的な考え方を示すことを目的としている。ジェネリックプログラミングはC++の構成要素であり、孤立したサブ言語ではない。最後に、本論文は、使用パターン、オブジェクト指向プログラミングとの関係、値パラメータ、シンボル表現、コンセプト型マッチング、および定義チェックを含む、コンセプト設計の重要部分の設計理念と起源を提供する。
ジェネリックプログラミングの課題 :従来のC++ジェネリックプログラミングは明確なインターフェース仕様を欠いており、コンパイル時エラーメッセージが不明確であり、プログラマーとコンパイラーの両方がテンプレートインターフェースを理解して使用することが困難である。型安全性の問題 :C++はC言語の暗黙的型変換規則を継承しており、特に算術型間の狭小化変換(例えば、大きい整数型から小さい整数型への変換は情報を失う可能性がある)は、エラーとセキュリティ問題の重要な原因である。範囲チェックの欠如 :従来のポインタと配列の使用は、バッファオーバーフローなどのセキュリティ問題を引き起こしやすく、効果的な範囲チェック機構を欠いている。本論文の中核的な動機は、C++20で導入されたコンセプト(concepts)を使用して以下を実現する方法を示すことである:
静的型安全なインターフェース仕様を提供する ゼロオーバーヘッドの型安全性を実装する ユーザー定義型システム拡張を構築する ジェネリックプログラミングと汎用プログラミングの統一性を維持する 論文はAlex Stepanovが提唱したジェネリックプログラミングの目標「最も汎用的で、最も効率的で、最も柔軟な概念表現」に従い、以下の設計要件を満たす:
汎用性 :想像を超えるより多くのコンテンツを表現できる必要がある妥協のない効率性 :ジェネリックコードは同等の低レベルコードと比較して実行時オーバーヘッドを生じるべきではない静的型安全インターフェース :型システムは十分に柔軟である必要があり、コンパイル時チェックが実行時値に依存しないインターフェースのほとんどの側面を許可するコンセプトベースの型安全プログラミング技法を提案 :狭小化変換を排除し、ゼロ実行時オーバーヘッドを維持しながら範囲チェックを提供する方法を示した。実用的な型システム拡張を構築 :危険な狭小化変換を排除するNumber<T>型を実装 範囲チェック付き配列アクセスを提供する安全なSpan型を設計 アルゴリズム設計におけるコンセプトの応用を示した コンセプト設計の深い理念を提供 :コンパイル時関数としてのコンセプトの設計決定、オブジェクト指向プログラミングとの関係、シンボル表現選択などの重要な設計考慮事項を詳細に説明した。ジェネリックプログラミングの統一性を示した :ジェネリックプログラミングはC++の構成要素であり、孤立したサブ言語ではなく、ラムダ、可変長テンプレート、静的リフレクションなどの他の言語機能とシームレスに統合されることを証明した。コンセプトはコンパイル時に評価される関数(述語)であり、型パラメータを受け入れることができる:
template<typename T>
concept Num = integral<T> || floating_point<T>;
template<typename T, typename U>
concept Can_narrow_to =
! same_as<T, U> // 異なる型
&& Num<T> && Num<U> // 両方とも数値型
&& (
(floating_point<T> && integral<U>) // 小数部を失う可能性
|| (numeric_limits<T>::digits > numeric_limits<U>::digits) // 切り詰められる可能性
|| (signed_integral<T>!=signed_integral<U> && sizeof(T)==sizeof(U)) // 符号が変わる可能性
);
template<Num U, Num T>
constexpr bool will_narrow(U u) {
if constexpr (!Can_narrow_to<T, U>)
return false;
// 狭小化の可能性がある場合のみ実行時チェック
T t = u;
return (t != u);
}
template<Num T>
class Number {
T val;
public:
template<Num U>
constexpr Number(const U u) : val{convert_to<T>(u)} { }
operator T() { return val; }
};
template<class T>
class Span {
T* p;
unsigned n;
public:
Span(Spanable auto& s) : p{data(s)}, n{size(s)} {}
T& operator[](Number<unsigned> i) {
return p[check(i)];
}
private:
unsigned check(unsigned nn) {
if (n <= nn) throw Span_range_error{};
return nn;
}
};
template<typename R, typename Pred = ranges::less>
concept Sortable_range =
ranges::random_access_range<R>
&& sortable<ranges::iterator_t<R>, Pred>;
template<typename R, typename Pred = ranges::less>
requires Sortable_range<R,Pred>
void sort(R& r, Pred p = {}) {
sort(r.begin(), r.end(), p);
}
コンセプトは関数集合のようなクラス定義ではなく、使用パターンを通じて要件を定義する:
template<typename T, typename U = T>
concept equality_comparable = requires(T a, U b) {
{a==b} -> Boolean;
{a!=b} -> Boolean;
{b==a} -> Boolean;
{b!=a} -> Boolean;
}
このアプローチの利点:
混合モード算術を処理する 暗黙的変換を処理する インターフェース安定性を提供する コンセプトは型の型または型の属性ではなく、コンパイル時関数として設計されている:
型の属性を問い合わせることができる(「あなたはイテレータですか?」) 複数のパラメータを受け入れることができる コンパイル時に実行され、効率が高い コンパイル時チェックと条件付きコンパイルを通じて、ゼロ実行時オーバーヘッドを実現する:
template<Num U, Num T>
constexpr bool will_narrow(U u) {
if constexpr (!Can_narrow_to<T, U>)
return false; // コンパイル時に決定、実行時コストなし
// 必要な場合のみ実行時チェック
}
論文は多数の実際のコード例を通じて方法の有効性を検証する:
算術変換の例 :Number型がどのように狭小化変換を防ぐかを示す範囲アクセスの例 :Span型がどのように安全な配列アクセスを提供するかを示すソートアルゴリズムの例 :コンセプトがどのようにアルゴリズムインターフェースの明確性と安全性を改善するかを示すコンパイル時チェックは実行時オーバーヘッドを生じない 潜在的な狭小化の場合のみ実行時チェックを実行 基礎となるコードと同じパフォーマンス特性を維持 型安全性の向上 :Number<int> test() {
Number<unsigned int> ii = 0;
ii = 2; // OK
ii = -2; // throws - 符号エラーをキャッチ
}
範囲安全性 :Span<int> sa{array};
int x = sa[10]; // 範囲内の場合OK
int y = sa[-1]; // throws - 負のインデックスエラーをキャッチ
アルゴリズムインターフェースの改善 :sort(vec); // 簡潔なインターフェース
sort(lst); // コンパイル時エラー:listはランダムアクセスをサポートしない
コンパイル時チェック:ゼロ実行時オーバーヘッド 実行時チェック:必要な場合のみ実行、最小限のオーバーヘッド 最適化に適した:インライン化とその他のコンパイラ最適化をサポート STLコンセプト :イテレータ、シーケンス、コンテナ(1990年代初期)数学的コンセプト :モノイド、群、環、体(数百年の歴史)グラフ理論コンセプト :辺、頂点、グラフ、DAG(1736年以降)2003年:初回コンセプト提案 2009年:C++0xコンセプトが削除 2017年:コンセプトが再導入 2020年:C++20に正式に含まれる 論文は、多くの制約システムが関数集合(クラス定義に類似)に依存しているのに対し、C++コンセプトは関数型アプローチを使用し、より大きな柔軟性を提供することを指摘している。
コンセプトはコンパイル時関数である :この設計は柔軟性と効率の最適なバランスを提供する。ジェネリックプログラミングとOOPは相補的 :GPは関数(アルゴリズム)とパラメータ要件に焦点を当てる OOPはオブジェクトとインターフェース実装に焦点を当てる 両者は効果的に組み合わせて使用できる 統一されたプログラミングモデル :ジェネリックプログラミングは孤立したサブ言語ではなく、C++の構成要素である。コンセプト :テンプレートがそのパラメータに対して実行できる操作を指定するクラス階層 :事前定義されたインターフェース、予見性が必要コンセプトはより柔軟 :要件を満たす任意の型の組み合わせを使用できる// 理想的な関数型構文(採用されなかった)
void sort(Sortable_range& r);
// 実際に採用された妥協構文
void sort(Sortable_range auto& r);
公理(Axioms) :アナライザーとコード生成器のための構造の意味的性質を指定出力範囲 :範囲安全性を増加させ、出力操作に対して範囲チェックを実行シンボル表現の改善 :コンセプト後のauto冗長性を排除クラスのオーバーロード :コンセプトに基づくクラスオーバーロードを許可パターンマッチング :関数型プログラミングスタイルのパターンマッチング統一関数呼び出し :関数型とOOP型呼び出し構文の統一実用性が高い :直接適用可能なプログラミング技法を提供し、実際の型安全性の問題を解決する。理論と実践の結合 :深い理論的基礎(数学的コンセプト)と具体的な実装例の両方を持つ。ゼロオーバーヘッド設計 :巧妙なコンパイル時チェック設計を通じて、型安全性を実現しながらパフォーマンス損失がない。包括的な設計考慮 :基本構文から高度な機能(静的リフレクションなど)までの完全な設計空間をカバー。権威性 :著者Bjarne StroustrupはC++言語の創造者であり、疑いの余地のない権威性を持つ。学習曲線が急峻 :コンセプトの構文と使用パターンは初心者にとって複雑である可能性がある。コンパイル時間への影響 :大量のコンパイル時チェックはコンパイル時間を増加させる可能性があり、論文ではこれについて十分に議論されていない。後方互換性の課題 :互換性は維持されているが、新旧コードの混合使用時に混乱が生じる可能性がある。エラーメッセージの品質 :コンセプトはエラーメッセージを改善しているが、複雑なコンセプトのエラーメッセージは依然として理解しにくい可能性がある。C++コミュニティへの影響 :C++20コンセプトの実際の応用に対して権威的なガイダンスを提供する。他の言語への啓発 :コンセプト設計理念は他の言語の制約システム設計に影響を与える可能性がある。教育的価値 :ジェネリックプログラミング教育のための優れた教材を提供する。実用的価値 :提供される技法はコードの型安全性を向上させるために直接使用できる。システムレベルプログラミング :高パフォーマンスと型安全性が必要な低レベルコード。ライブラリ設計 :ジェネリックライブラリに対して明確なインターフェース仕様を提供。教育と訓練 :ジェネリックプログラミングの概念と技法の教材として。コード リファクタリング :既存の安全でないコードを型安全なバージョンにリファクタリング。論文は、C++言語設計からジェネリックプログラミング理論に至るまで、様々な側面をカバーする豊富な参考文献を含む。主なものは以下の通り:
Alex StepanovによるSTLとジェネリックプログラミングに関する開拓的な業績 C++標準委員会の技術報告書と提案 著者自身によるC++設計と進化に関する歴史的文書 関連するプログラミング言語理論研究 要約 :これは重要な理論的および実践的価値を持つ論文であり、C++言語の創造者自身によって執筆され、C++ジェネリックプログラミングにおけるコンセプトの応用を包括的に示している。論文は実用的なプログラミング技法を提供するだけでなく、設計理念を深く説明しており、C++プログラマーとプログラミング言語研究者の両方にとって重要な参考価値を持つ。