Skip to content

Commit 355f751

Browse files
committed
C++ Literals: Codes numériques et Unicode
1 parent 5eadc91 commit 355f751

3 files changed

Lines changed: 296 additions & 16 deletions

File tree

_articles/c++_auto.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ category: c++
66
logo: c++.svg
77
background: corridor4.webp
88
seo:
9-
title: "Évolutions du mot clef auto du C++98 à C++26"
9+
title: "Maîtriser complètement auto en C++" # "Évolutions du mot clef auto du C++98 à C++26"
1010
description: "Toutes les utilisations, évolutions, cas particuliers, pièges et bonnes pratiques du mot clef auto en C++."
1111
published: true
1212
reviewers:
@@ -16,7 +16,7 @@ reviewers:
1616
link: https://github.com/glcraft
1717
---
1818

19-
Le mot clef ``auto``, ses **avantages** et ses différents comportements **selon la version** et le contexte.
19+
Le mot clef ``auto`` de C++98 à C++26, ses **avantages** et ses différents comportements **selon la version** et le contexte.
2020

2121
{% reviewers %}
2222

_articles/c++_literals.md

Lines changed: 82 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,7 @@ auto number2 = 0123; // int exprimé en octal (Base 8) (commence par 0)
103103
auto number3 = 123'456'789; // int avec des séparateurs (C++14)
104104
{% endhighlight %}
105105

106-
> **Séparateurs de chiffres**: Les apostrophes (``'``) peuvent être placées librement dans le nombre. Elles n'ont aucun impact sur la valeur et sont ignorées par le compilateur. Leur seul but est d'améliorer la lisibilité des grands nombres.
106+
Les apostrophes (``'``) peuvent être placées librement dans le nombre. Elles n'ont **aucun impact** sur la valeur et sont ignorées par le compilateur. Leur seul but est d'améliorer la **lisibilité des grands nombres**.
107107

108108
> [**Attention à l'octal**](#integer-literal): Un nombre commençant par **``0``** est interprété comme de l'**octal** (Base 8).<br>
109109
> C'est un piège classique: ``0123`` en octal vaut **``83`` en décimal** (``1*8² + 2*8¹ + 3*8⁰ = 83``).<br>
@@ -132,11 +132,11 @@ Le C++23 a introduit des suffixes spécifiques pour faciliter la manipulation de
132132
| **``uz``** (et variantes) | [**``std::size_t``**](/articles/c++/std_size_t) | Type non signé pour les tailles d'objets. |
133133
| **``z``**, **``Z``** | ``std::make_signed_t<std::size_t>`` | Version signée de ``std::size_t`` (souvent identique à ``std::ptrdiff_t``). |
134134

135-
> **Combinaisons de suffixes (C++23)** : Pour ``std::size_t``, n'importe quelle combinaison de **``z``** (ou ``Z``) et de **``u``** (ou ``U``) est valide : ``zu``, ``zU``, ``Zu``, ``ZU``, ``uz``, ``uZ``, ``Uz`` ou ``UZ``.
135+
Pour ``std::size_t``, n'importe quelle combinaison de **``z``** (ou ``Z``) et de **``u``** (ou ``U``) est valide : ``zu``, ``zU``, ``Zu``, ``ZU``, ``uz``, ``uZ``, ``Uz`` ou ``UZ``.
136136

137-
> Le type signé produit par **``z``** est l'équivalent standard du type [**``ssize_t`` (POSIX)**](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html). Il est très utile pour les boucles décrémentales afin d'éviter les débordements (underflow) des types non signés.
137+
Le type signé produit par **``z``** est l'[**équivalent standard**](/articles/c++/size#les-alternatives-signées--ptrdiff_t-et-stdssize) du type [**``ssize_t`` (POSIX)**](https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/sys_types.h.html). Il est très utile pour les boucles décrémentales afin d'éviter les débordements (underflow) des types non signés.
138138

139-
En revanche, il n'existe toujours pas de literal pour les types à **largeur fixe** stricte (comme ``std::int64_t`` ou ``std::int32_t``). On continue d'utiliser les literals des types fondamentaux correspondants :
139+
En revanche, il n'existe toujours pas de literal pour les types à **largeur fixe** stricte (comme ``std::int64_t`` ou ``std::int32_t``). On continue d'utiliser les literals des types fondamentaux correspondants:
140140
{% highlight cpp %}
141141
auto n64 = 42ll; // Type long long int (au moins 64 bits)
142142
auto u64 = 42ull; // Type unsigned long long int (au moins 64 bits)
@@ -292,7 +292,9 @@ Elle reste présente [dans les includes de headers C portés en C++](https://en.
292292
Le **C++11** se voit ajouter le **literal ``nullptr``** (oui, c'est bien un literal et pas une constante comme [``nullptr`` en C](#en-c-depuis-c23-nullptr)), version à partir de laquelle ``NULL`` est **redéfini en ``#define NULL nullptr``** pour bénéficier tout de même du **typage fort** sur les codes historiques.
293293

294294
> Bien que ``nullptr`` en C++11 et ``nullptr`` en C23 **partagent le même nom** et remplissent un rôle similaire (valeur nulle non ambiguë), ce sont deux entités distinctes, **chacune définie dans son langage respectif** avec des mécanismes internes différents.<br>
295+
>
295296
> Il en est de même pour ``std::nullptr_t`` en C++11 et ``nullptr_t`` en C23. Le premier étant un type propre au langage C++ **importé depuis ``<cstddef>``** tandis que le second est un **type fondamental** du langage C.<br>
297+
>
296298
> Ils représentent la même notion et partagent des comportements similaires, **mais ne sont ni la même valeur, ni le même type partagé entre les deux langages**.<br>
297299
> Une **interopérabilité** reste cependant **possible, mais pas automatique**.<br>
298300
> Ceci requiert l'utilisation de ``extern "C"``.
@@ -393,8 +395,7 @@ Ces séquences sont utilisables aussi bien dans les [**character literal**](#cha
393395

394396
> Notez qu'il n'est pas nécessaire d'échapper un double guillemet à l'intérieur de guillemets simples (``'"'`` est valide), ni un guillemet simple à l'intérieur de guillemets doubles (``"'"`` est valide). L'échappement n'est requis que pour lever une ambiguïté.
395397
396-
Voici la liste complète des [séquences d'échappement reconnues par le standard](https://en.cppreference.com/w/cpp/language/escape):
397-
398+
Voici la liste des [**séquences d'échappement simples**](https://en.cppreference.com/w/cpp/language/escape):
398399

399400
| Séquence | Signification |
400401
| :--- | :--- |
@@ -409,14 +410,52 @@ Voici la liste complète des [séquences d'échappement reconnues par le standar
409410
| ``\'`` | Guillemet simple |
410411
| ``\"`` | Guillemet double |
411412
| ``\?`` | Point d'interrogation |
412-
| **``\0``** | Caractère nul utilisé comme **sentinelle** (valant **0**, équivalent à [**``NULL``**](#en-c-avant-c23-null) en langage C) |
413-
| **``\nnn``** | Code **octal** (ex: ``\123``) |
414-
| **``\xnn``** | Code **hexadécimal** (ex: ``\x53``) |
415-
| **``\unnnn``** | Code **Unicode** (UTF-16) |
416-
| **``\Unnnnnnnn``** | Code **Unicode** (UTF-32) |
413+
| ``\0`` | Caractère nul utilisé comme **sentinelle** (valant **0**, équivalent à [**``NULL``**](#en-c-avant-c23-null) en langage C) |
417414

418415
> **Note sur le signal sonore (``\a``)**: L'émission d'un son dépend de votre terminal et de sa configuration. Le caractère est envoyé au flux de sortie, mais c'est à l'émulateur de terminal de décider s'il doit déclencher un "beep" système ou une notification.
419416
417+
#### Codes numériques et Unicode
418+
419+
Les [**universal character names**](https://en.cppreference.com/w/cpp/language/escape) (tableau ci-dessous) permettent de référencer un caractère par son [*code point*](https://www.unicode.org/versions/Unicode17.0.0/core-spec/chapter-2/#G25564) Unicode. Ils **évitent toute dépendance** à l'**encodage du fichier** source.
420+
421+
Sans ces codes, un caractère écrit "**en dur**" peut être **interprété différemment selon la machine** qui compile:
422+
{% highlight cpp %}
423+
// Le résultat dépend de l'encodage du fichier source
424+
auto s = "é";
425+
// Si le fichier source est en UTF-8 → C3 A9
426+
// Si le fichier source est en Windows-1252 → E9
427+
428+
// Avec un universal character name
429+
auto s = "\u00E9"; // // garantie de référencer le code point Unicode U+00E9
430+
{% endhighlight %}
431+
432+
En revanche, **l'encodage final** du caractère dans le binaire **dépend du type de literal** ("", [u8""](#string-literal), [u""](#string-literal), [U""](#string-literal)).<br>
433+
Seuls **les literals UTF** (u8, u, U) **garantissent l'encodage** exact des octets générés.
434+
435+
| Séquence | Signification |
436+
| :--- | :--- |
437+
| ``\nnn`` | Valeur **octale** (1 à 3 chiffres, ex: ``\123``) |
438+
| ``\xn...`` | Valeur **hexadécimale**. ``\x`` suivi d'un **nombre arbitraire de chiffres** hexadécimaux (ex: ``\x53``), **jusqu'à un caractère non-hexadécimal** |
439+
| ``\unnnn`` | Code **Unicode**. ``\u`` suivi de **4 chiffres** hexadécimaux |
440+
| ``\Unnnnnnnn`` | Code **Unicode**. ``\U`` suivi de **8 chiffres** hexadécimaux |
441+
| ``\o{n...}`` | **Octal délimité**¹ (C++23) |
442+
| ``\x{n...}`` | **Hexadécimal délimité**¹ (C++23) |
443+
| ``\u{n...}`` | **Unicode délimité**¹ (C++23) exprimé en hexadécimal |
444+
| ``\N{NAME}`` | **Caractère nommé** (C++23). Permet d'utiliser un caractère par son nom officiel Unicode. |
445+
446+
*¹Le terme "**délimité**" signifie que la valeur est **entourée d'accolades**. Celles-ci peuvent contenir un **nombre arbitraire de chiffres**. Cette syntaxe permet une longueur libre et **évite que le compilateur ne "mange" par erreur des chiffres** appartenant au texte qui suit.*
447+
448+
{% highlight cpp %}
449+
auto s0 = "\123"; // 'S' (Octal)
450+
auto s1 = "\x53"; // 'S' (Hexadécimal)
451+
452+
auto s2 = "\u{1F600}"; // 😀 (Unicode délimité)
453+
auto s3 = "\x{41}"; // 'A' (Hexadécimal délimité)
454+
455+
auto s4 = "\N{GREEK DELTA}"; // Δ
456+
auto s5 = "\N{GRINNING FACE}"; // 😀
457+
{% endhighlight %}
458+
420459
### Multi-caractères (Multi-character literal)
421460

422461
Mettre plusieurs caractères entre guillemets simples est une pratique **fortement déconseillée**:
@@ -447,7 +486,7 @@ auto s3 = U"hello"; // const char32_t[6] (UTF-32) (Depuis C++11)
447486
auto s4 = L"hello"; // const wchar_t[6] (Wide character)
448487
{% endhighlight %}
449488

450-
> Chaque literal de chaîne, quel que soit son encodage, se termine par une **sentinelle nulle** (``\0``) ajoutée automatiquement par le compilateur. La taille du tableau (``[6]`` ici) inclut toujours ce caractère invisible.
489+
Chaque literal de chaîne, quel que soit son encodage, se termine par une **sentinelle nulle** (``\0``) ajoutée automatiquement par le compilateur. La taille du tableau (``[6]`` ici) inclut toujours ce caractère invisible.
451490

452491
### ``std::string`` literal (C++14) et ``std::string_view`` literal (C++17)
453492

@@ -513,7 +552,7 @@ auto sv = u"hello"sv; // std::u16string_view
513552
> À noter que la plupart de ces littéraux sont devenus [**``constexpr``**](/articles/c++/compile-time_execution) à partir du C++20.
514553
515554

516-
> **Sécurité de string_view**: Par nature, un ``std::string_view`` ne garantit pas la présence d'un ``\0`` final (il se contente d'un pointeur et d'une taille). Cependant, lorsqu'il est construit à partir d'un **literal** (``"..."sv``), il pointe vers le tableau statique du binaire qui, lui, possède bien cette sentinelle. Passer ``sv.data()`` à une fonction attendant un pointeur C est donc **techniquement sûr** dans ce cas précis, bien que risqué conceptuellement.
555+
Par nature, un ``std::string_view`` ne garantit pas la présence d'un ``\0`` final (il se contente d'un pointeur et d'une taille). Cependant, lorsqu'il est construit à partir d'un **literal** (``"..."sv``), il pointe vers le tableau statique du binaire qui, lui, possède bien cette sentinelle. Passer ``sv.data()`` à une fonction attendant un pointeur C est donc **techniquement sûr** dans ce cas précis, bien que risqué conceptuellement.
517556

518557
### String literal: lvalue ou prvalue ?
519558

@@ -663,6 +702,8 @@ Les opérateurs de literals ne peuvent prendre [**que des types spécifiques**](
663702
- **Caractères** : ``char``, ``wchar_t``, ``char8_t`` (C++20), ``char16_t``, ``char32_t``.
664703
- **Chaînes** : Un couple ``(const T*, std::size_t)````T`` est l'un des types de caractères ci-dessus.
665704

705+
> **Conflits de suffixes**: Il est possible d'utiliser le même suffixe pour des catégories différentes (ex: le suffixe ``s`` utilisé pour les [**chaînes**](#stdstring-literal-c14-et-stdstring_view-literal-c17) (``"hello"s``) et pour [**chrono**](#chrono-literal-c14--c20) (``1s``)). Le compilateur ne rencontre aucune ambiguïté car il distingue les catégories par leurs paramètres (un couple ``ptr, size`` pour une chaîne contre un ``unsigned long long`` pour un nombre).
706+
666707
> **Le cas particulier du ``const char*``**: Un opérateur ne prenant qu'un simple ``const char*`` est utilisé **uniquement** pour les literals numériques en mode "Raw". Il n'existe pas d'équivalent pour les chaînes de caractères (**qui exigent toujours la taille en second paramètre**).
667708
>
668709
> Ainsi, si vous définissez un ``operator ""_suffix(const char*)``:
@@ -715,9 +756,36 @@ void operator ""_print(const char* rawString)
715756
42_print; // Affiche "Literal brut: 42"
716757
{% endhighlight %}
717758

759+
## Combinaison extrême
760+
761+
Pour illustrer la complexité de l'analyse lexicale du C++, voici un littéral qui combine presque toutes les fonctionnalités abordées dans cet article en une seule valeur:
762+
763+
{% highlight cpp %}
764+
auto x = 0x14'2.e9'1'fp-1'3_e\u00e9l\u{d4}f;
765+
{% endhighlight %}
766+
767+
Décortiquons rigoureusement ce "monstre" syntaxique:
768+
769+
1. **Préfixe ``0x``** : Indique que la valeur est exprimée en **hexadécimal**
770+
2. **[Mantisse](https://fr.wikipedia.org/wiki/Mantisse) ``14'2.e9'1'f``**:
771+
- La partie entière est ``142`` (hex)
772+
- La partie fractionnaire est ``e91f`` (hex). ``e`` et ``f`` sont ici des chiffres hexadécimaux et non des suffixes
773+
- Des **séparateurs de chiffres** (``'``) sont insérés arbitrairement pour la "lisibilité"
774+
3. **Exposant ``p-1'3``**:
775+
- Le [**préfixe ``p``**](#hexadécimaux-à-virgule-flottante-c17) est **obligatoire pour les flottants hexadécimaux** et indique une **puissance de 2**
776+
- L'exposant ici est **-13** (exprimé en décimal)
777+
4. [**Suffixe UDL**](#user-defined-literal-depuis-c11) ``_e\u00e9l\u{d4}f`` (``operator""_eélÔf``):
778+
- L'identifiant commence par un underscore (``_``)
779+
- Le caractère 'e'
780+
- Une séquence Unicode classique: [**``\u00e9``**](#codes-numériques-et-unicode) (lettre ``é``)
781+
- Le caractère 'l'
782+
- Une séquence Unicode délimitée (C++23): [**``\u{d4}**](#codes-numériques-et-unicode) (lettre ``Ô``)
783+
- Le caractère 'f'
784+
718785
---
719786

720787
Aller plus loin:
721788
- [Auto](/articles/c++/auto)
789+
- [Encodages](/articles/c++/encoding)
722790
- [Types Fondamentaux](/articles/c++/fundamental_types)
723-
- [Tailles (std::size_t)](/articles/c++/std_size_t)
791+
- [Les tailles en C++ (std::size_t)](/articles/c++/std_size_t)

0 commit comments

Comments
 (0)