2025-11-16T02:49:12.114465

Concept-Based Generic Programming in C++

Stroustrup
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.
academic

Programmation Générique Basée sur les Concepts en C++

Informations de Base

  • ID de l'article : 2510.08969
  • Titre : Concept-Based Generic Programming in C++
  • Auteur : Bjarne Stroustrup (Université Columbia)
  • Classification : cs.PL cs.SE
  • Date de publication : 2025
  • Lien de l'article : https://arxiv.org/abs/2510.08969

Résumé

Cet article démontre les techniques de programmation utilisant les concepts pour illustrer les installations et principes de la programmation générique en C++. Les concepts constituent la méthode d'expression des contraintes du code générique en C++. En guise d'exemple initial, l'article fournit un système de types simple qui élimine les conversions réductrices et offre une vérification des plages sans surcharge inutile de symboles ou de runtime. Les concepts sont largement utilisés pour fournir des extensions de systèmes de types définis par l'utilisateur. L'article vise à démontrer l'utilité pratique des concepts et les idées fondamentales qui les sous-tendent, plutôt que de fournir une explication détaillée ou complète du support du langage de programmation générique en C++ ou du support étendu de la bibliothèque standard. La programmation générique est une composante intégrante de C++, et non un sous-langage isolé. Enfin, l'article présente la philosophie de conception et les origines des éléments clés de la conception des concepts, notamment les modèles d'utilisation, la relation avec la programmation orientée objet, les paramètres de valeur, la représentation symbolique, l'appariement des types de concepts et la vérification des définitions.

Contexte et Motivation de la Recherche

Contexte du Problème

  1. Défis de la programmation générique : La programmation générique traditionnelle en C++ manque de spécifications d'interface explicites, ce qui entraîne des messages d'erreur de compilation obscurs, rendant difficile la compréhension et l'utilisation des interfaces de modèles pour les programmeurs et les compilateurs.
  2. Problèmes de sécurité des types : C++ hérite des règles de conversion de types implicites du langage C, en particulier les conversions réductrices entre types arithmétiques (comme la conversion d'un entier grand vers un entier petit pouvant perdre des informations), ce qui constitue une source importante d'erreurs et de problèmes de sécurité.
  3. Absence de vérification des plages : L'utilisation traditionnelle des pointeurs et des tableaux est susceptible de causer des débordements de tampon et autres problèmes de sécurité, avec l'absence de mécanismes efficaces de vérification des plages.

Motivation de la Recherche

La motivation centrale de cet article est de démontrer comment utiliser les concepts introduits en C++20 pour :

  • Fournir des spécifications d'interface de type statiquement sûres
  • Implémenter une sécurité des types sans surcharge
  • Construire des extensions de systèmes de types définis par l'utilisateur
  • Maintenir l'unité entre la programmation générique et la programmation générale

Objectifs de Conception

L'article suit l'objectif de programmation générique proposé par Alex Stepanov : « la représentation la plus générale, la plus efficace et la plus flexible des concepts », et satisfait aux exigences de conception suivantes :

  • Généralité : Doit être capable d'exprimer bien plus que ce qu'on peut imaginer
  • Efficacité sans compromis : Le code générique ne doit pas produire de surcharge runtime par rapport au code bas niveau équivalent
  • Interface de type statiquement sûre : Le système de types doit être suffisamment flexible pour permettre la vérification à la compilation de la plupart des aspects des interfaces qui ne dépendent pas des valeurs runtime

Contributions Principales

  1. Présentation de techniques de programmation de type sûr basées sur les concepts : Démontre comment utiliser les concepts pour éliminer les conversions réductrices et fournir une vérification des plages tout en maintenant une surcharge runtime nulle.
  2. Construction d'extensions de systèmes de types pratiques :
    • Implémentation du type Number<T> éliminant les conversions réductrices dangereuses
    • Conception du type Span sûr fournissant un accès aux tableaux avec vérification des plages
    • Démonstration de l'application des concepts dans la conception d'algorithmes
  3. Fourniture d'une compréhension approfondie de la conception des concepts : Exposition détaillée des décisions de conception des concepts en tant que fonctions de compilation, de la relation avec la programmation orientée objet, des choix de représentation symbolique et autres considérations de conception clés.
  4. Démonstration de l'unité de la programmation générique : Preuve que la programmation générique est une composante intégrante de C++, et non un sous-langage isolé, s'intégrant de manière transparente avec d'autres caractéristiques du langage (comme les lambdas, les modèles variadiques, la réflexion statique).

Explication Détaillée des Méthodes

Définition Fondamentale des Concepts

Les concepts sont des fonctions (prédicats) évaluées à la compilation, pouvant accepter des paramètres de type :

template<typename T>
concept Num = integral<T> || floating_point<T>;

Méthode d'Élimination des Conversions Réductrices

Définition du Concept de Détection des Réductions

template<typename T, typename U>
concept Can_narrow_to = 
    ! same_as<T, U>    // types différents
    && Num<T> && Num<U>   // tous deux de types numériques
    && ( 
        (floating_point<T> && integral<U>) // peut perdre la partie fractionnaire
        || (numeric_limits<T>::digits > numeric_limits<U>::digits) // peut tronquer
        || (signed_integral<T>!=signed_integral<U> && sizeof(T)==sizeof(U)) // peut changer le signe
    );

Fonction de Vérification Runtime

template<Num U, Num T>
constexpr bool will_narrow(U u) {
    if constexpr (!Can_narrow_to<T, U>)
        return false;  
    // vérification runtime uniquement si réduction possible
    T t = u;
    return (t != u);
}

Implémentation du Type Number

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; }
};

Implémentation de l'Accès Sécurisé aux Plages

Conception du Type Span

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;
    }
};

Application des Concepts dans les Algorithmes

Fonction de Tri Contrainte par Concepts

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);
}

Points d'Innovation Technique

1. Modèles d'Utilisation (Use Patterns)

Les concepts définissent les exigences par des modèles d'utilisation plutôt que par des ensembles de fonctions similaires aux définitions de classes :

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;
}

Avantages de cette approche :

  • Gestion des modèles arithmétiques mixtes
  • Gestion des conversions implicites
  • Stabilité de l'interface

2. Concepts en tant que Fonctions

Les concepts sont conçus comme des fonctions de compilation plutôt que comme des types de types ou des propriétés de types :

  • Possibilité d'interroger les propriétés des types (« êtes-vous un itérateur ? »)
  • Acceptation de plusieurs paramètres
  • Exécution à la compilation, très efficace

3. Abstraction Sans Surcharge

Réalisation d'une surcharge runtime nulle par vérification à la compilation et compilation conditionnelle :

template<Num U, Num T>
constexpr bool will_narrow(U u) {
    if constexpr (!Can_narrow_to<T, U>)
        return false;  // déterminé à la compilation, sans coût runtime
    // vérification runtime uniquement si nécessaire
}

Configuration Expérimentale

Vérification par Exemples de Code

L'article valide l'efficacité de la méthode par de nombreux exemples de code pratiques :

  1. Exemples de conversions arithmétiques : Démonstration de la manière dont le type Number prévient les conversions réductrices
  2. Exemples d'accès aux plages : Démonstration de la manière dont le type Span fournit un accès sécurisé aux tableaux
  3. Exemples d'algorithmes de tri : Démonstration de la manière dont les concepts améliorent la clarté et la sécurité des interfaces d'algorithmes

Considérations de Performance

  • Les vérifications à la compilation ne produisent pas de surcharge runtime
  • Les vérifications runtime ne sont effectuées que dans les cas de réduction potentielle
  • Maintien des mêmes caractéristiques de performance que le code sous-jacent

Résultats Expérimentaux

Réalisations Principales

  1. Amélioration de la Sécurité des Types :
    Number<int> test() {
        Number<unsigned int> ii = 0;
        ii = 2;   // OK
        ii = -2;  // lève une exception - capture l'erreur de signe
    }
    
  2. Sécurité des Plages :
    Span<int> sa{array};
    int x = sa[10];   // OK si dans la plage
    int y = sa[-1];   // lève une exception - capture l'erreur d'index négatif
    
  3. Amélioration des Interfaces d'Algorithmes :
    sort(vec);  // interface concise
    sort(lst);  // erreur de compilation : list ne supporte pas l'accès aléatoire
    

Caractéristiques de Performance

  • Vérification à la compilation : surcharge runtime nulle
  • Vérification runtime : effectuée uniquement si nécessaire, surcharge minimale
  • Optimisation favorable : support de l'inlining et autres optimisations du compilateur

Travaux Connexes

Historique de la Programmation Générique

  • Concepts STL : itérateurs, séquences, conteneurs (début des années 1990)
  • Concepts mathématiques : monoïdes, groupes, anneaux, corps (histoire de plusieurs siècles)
  • Concepts de théorie des graphes : arêtes, sommets, graphes, DAG (depuis 1736)

Évolution des Concepts en C++

  • 2003 : première proposition de concepts
  • 2009 : suppression des concepts de C++0x
  • 2017 : réintroduction des concepts
  • 2020 : inclusion officielle des concepts en C++20

Comparaison avec d'Autres Systèmes de Contraintes

L'article souligne que de nombreux systèmes de contraintes dépendent d'ensembles de fonctions (similaires aux définitions de classes), tandis que les concepts C++ utilisent une approche fonctionnelle, offrant une plus grande flexibilité.

Conclusions et Discussion

Conclusions Principales

  1. Les concepts sont des fonctions de compilation : Cette conception offre le meilleur équilibre entre flexibilité et efficacité.
  2. La programmation générique et la POO sont complémentaires :
    • La PG se concentre sur les fonctions (algorithmes) et les exigences de paramètres
    • La POO se concentre sur les objets et l'implémentation des interfaces
    • Les deux peuvent être utilisées efficacement ensemble
  3. Modèle de programmation unifié : La programmation générique n'est pas un sous-langage isolé, mais une composante intégrante de C++.

Philosophie de Conception

Concepts vs Hiérarchies de Classes

  • Concepts : Spécifient les opérations que le modèle doit pouvoir effectuer sur ses paramètres
  • Hiérarchies de classes : Interfaces prédéfinies, nécessitant une prévoyance
  • Les concepts sont plus flexibles : Peuvent utiliser n'importe quelle combinaison de types satisfaisant les exigences

Choix de Représentation Symbolique

// Syntaxe fonctionnelle idéale (non adoptée)
void sort(Sortable_range& r);

// Syntaxe de compromis réellement adoptée
void sort(Sortable_range auto& r);

Directions de Développement Futur

  1. Axiomes : Spécification des propriétés sémantiques des constructions pour les analyseurs et générateurs de code
  2. Plages de sortie : Augmentation de la sécurité des plages avec vérification des plages pour les opérations de sortie
  3. Amélioration de la représentation symbolique : Élimination de la redondance auto après les concepts
  4. Surcharge de classes : Permettre la surcharge de classes basée sur les concepts
  5. Correspondance de motifs : Correspondance de motifs de style programmation fonctionnelle
  6. Appel de fonction unifié : Unification de la syntaxe d'appel de fonction et de style POO

Évaluation Approfondie

Points Forts

  1. Praticité élevée : Fournit des techniques de programmation directement applicables, résolvant des problèmes réels de sécurité des types.
  2. Combinaison de théorie et de pratique : Possède à la fois une base théorique solide (concepts mathématiques) et des exemples d'implémentation concrets.
  3. Conception sans surcharge : Par le biais d'une conception astucieuse de vérification à la compilation, réalise la sécurité des types sans perte de performance.
  4. Considérations de conception complètes : Couvre l'espace de conception complet, de la syntaxe fondamentale aux caractéristiques avancées (comme la réflexion statique).
  5. Autorité : L'auteur Bjarne Stroustrup est le créateur du langage C++, possédant une autorité incontestable.

Limitations

  1. Courbe d'apprentissage abrupte : La syntaxe des concepts et les modèles d'utilisation peuvent être complexes pour les débutants.
  2. Impact sur le temps de compilation : Les nombreuses vérifications à la compilation peuvent augmenter le temps de compilation, sujet insuffisamment discuté dans l'article.
  3. Défis de compatibilité rétroactive : Bien que la compatibilité soit maintenue, le mélange de code ancien et nouveau peut créer de la confusion.
  4. Qualité des messages d'erreur : Bien que les concepts améliorent les messages d'erreur, les messages d'erreur pour les concepts complexes peuvent rester difficiles à comprendre.

Impact

  1. Impact sur la communauté C++ : Fournit des orientations faisant autorité pour l'application pratique des concepts C++20.
  2. Inspiration pour d'autres langages : La philosophie de conception des concepts peut influencer la conception de systèmes de contraintes dans d'autres langages.
  3. Valeur pédagogique : Fournit d'excellents matériaux pédagogiques pour l'enseignement de la programmation générique.
  4. Valeur pratique : Les techniques fournies peuvent être directement utilisées pour améliorer la sécurité des types du code.

Scénarios d'Application

  1. Programmation au niveau système : Code bas niveau nécessitant haute performance et sécurité des types.
  2. Conception de bibliothèques : Fournir des spécifications d'interface claires pour les bibliothèques génériques.
  3. Enseignement et formation : Matériel pédagogique pour les concepts et techniques de programmation générique.
  4. Refactorisation de code : Refactorisation du code existant non sûr en versions type-sûres.

Références Bibliographiques

L'article contient de riches références couvrant divers aspects du design du langage C++ à la théorie de la programmation générique, incluant principalement :

  • Les travaux fondateurs d'Alex Stepanov sur la STL et la programmation générique
  • Les rapports techniques et propositions du comité de normalisation C++
  • Les documents historiques de l'auteur sur la conception et l'évolution de C++
  • Les recherches connexes en théorie des langages de programmation

Résumé : Ceci est un article d'importance théorique et pratique majeure, rédigé personnellement par le créateur du langage C++, présentant de manière complète l'application des concepts dans la programmation générique en C++. L'article fournit non seulement des techniques de programmation pratiques, mais expose également en profondeur la philosophie de conception, possédant une valeur de référence importante pour les programmeurs C++ et les chercheurs en langages de programmation.