Introduction aux liaisons structurées en C++17
Les liaisons structurées (structured bindings) sont une fonctionnalité puissante introduite en C++17. Elles permettent de décomposer directement les membres d’un type agrégat (tableau, std::tuple, std::pair ou toute classe répondant à certaines conditions) en variables indépendantes. Cela améliore la lisibilité et simplifie l’accès aux données composites.
Syntaxe et cas d’usage de base
// Décomposition d'une paire
std::pair<int, double> getCoordinates() {
return {10, 2.718};
}
auto [x, y] = getCoordinates(); // x = 10, y = 2.718
Ici, x et y sont déduits et initialisés à partir des éléments de la paire.
Gestion des références
Pour obtenir des liaisons par référence plutôt que par copie, il faut explicitement utiliser auto& (référence lvalue) ou const auto& (référence constante).
std::tuple<int, std::string> t{42, "monde"};
const auto& [id, msg] = t; // id et msg sont des références vers t
Dans cet exemple, msg est une référence vers la chaîne interne du tuple ; toute modification de t se répercute sur msg.
| Type | Liaison structurée supportée ? | Remarque |
|---|---|---|
std::tuple |
Oui | Nécessite une fonction libre get<I>() ou un membre |
std::array |
Oui | Supporté comme agrégat |
| Struct simple | Oui (C++20 plus flexible) | En C++17, tous les membres doivent être publics et non statiques |
Principes fondamentaux des liaisons structurées et des références
Syntaxe et sémantique des références
auto [a, b] = std::make_pair(100, 'Z');
const auto& [c, d, e] = std::make_tuple(1, 2.5, "texte");
auto [a,b]: création par valeur (copie)const auto&: références constantes vers l’objet original, évitant les copies coûteuses
| Méthode | Partage-t-il la mémoire ? | Modifiable ? |
|---|---|---|
auto [x,y] |
Non | Oui (copie indépendante) |
auto& [x,y] |
Oui | Oui |
const auto& [x,y] |
Oui | Non |
Durée de vie de l’objet lié et validité des références
L’objet source doit survivre aux variables liées. Une référence pendante survient lorsque l’objet est détruit avant la fin d’utilisation.
// Exemple dangereux
const std::string& getTemp() {
return std::string("temporaire"); // retourne une référence à un objet local détruit
}
Les règles de prolongation de durée de vie s’appliquent uniquement lorsqu’une référence const est directement liée à un temporaire, pas lors d’un retour de fonction.
Gestion des lvalue et rvalue dans les liaisons
int val = 5;
int& lref = val; // OK : lvalue
int&& rref = 30; // OK : rvalue
int&& err = val; // Erreur : ne peut pas lier une rvalue ref à une lvalue
const int& cref = 42; // OK : const ref peut lier une rvalue (durée prolongée)
Comparaison std::tie vs liaisons structurées
std::tuple<int, std::string> getData() {
return {7, "hello"};
}
// std::tie nécessite des variables préexistantes (copie par affectation)
int n;
std::string s;
std::tie(n, s) = getData();
// Liaison structurée permet la création directe avec référence
auto& [a, b] = getData(); // a, b sont des références aux éléments du tuple temporaire ? Attention : durée de vie !
Note : auto& sur un temporaire est dangereux ; préférez const auto& ou une variable nommée.
Impact des implémentations des compilateurs
Certains compilateurs plus enciens (MSVC 2019) peuvent ne pas respecter parfaitement le prolongement de durée de vie.
const std::string& ref = std::string("temporaire") + std::string("prolongé");
std::cout << ref; // comportement indéfini sur certains compilateurs anciens
| Compilateur | Conformité | Prolongement ref const |
|---|---|---|
| GCC 10+ | Haute | Complet |
| Clang 9+ | Haute | Complet |
| MSVC 2019 | Moyenne | Partiel |
Pièges fréquents avec les références et erreurs à éviter
Références pendantes issues de temporaires
Quand une référence est liée à un temporaire dont la durée de vie n’est pas prolongée (par exemple via un retour de fonction), elle devient pendante.
// Exemple incorrect
const std::string& getTemp() {
return std::string("temporaire");
}
La règle de prolongation ne s’applique pas lors d’un retour : le temporaire est détruit avant la sortie de la fonction.
Membres de type référence dans une structure
Note : en C++, les membres référence peuvent créer des aliasing dangereux.
struct Paquet {
int& ref;
};
int x = 10;
Paquet p1{x};
Paquet p2 = p1; // p2.ref référence toujours x
p2.ref = 20;
// x vaut maintenant 20
Pour éviter le partage involontaire, préférez des pointeurs ou des copies profondes.
Risque des retours de fonction comme source de liaison
En utilisant une fonction qui retourne un objet temporaire comme source de liaison, la durée de vie peut être trop courte.
struct Data { int a; std::string b; };
Data makeData() { return {1, "test"}; }
void use() {
const auto& [v, w] = makeData(); // dangereux : le temporaire est détruit après la ligne ?
// w peut être une référence pendante
}
Solution : stocker d’abord le résultat dans une variable nommée.
Bonnes pratiques pour utiliser les références en toute sécurité
Garantir une durée de vie suffisante de l’objet source
Toujours s’assurer que l’objet lié vit au moins aussi longtemps que les variables liées.
// Correct
auto tuple = std::make_tuple(42, std::string("bonjour"));
const auto& [a, b] = tuple; // a et b référencent des membres de 'tuple' qui existe encore
Éviter les liaisons directes sur des temporaires non nommés :
// Risqué
const auto& [x, y] = std::make_tuple(1, 2); // temporaire détruit à la fin de l'expression complète (fin de ligne) ? En réalité, la durée de vie est prolongée car const auto& lie directement le temporaire. Mais attention au contexte complexe.
En cas de doute, utilisez une variable nommée.
Utiliser const auto& pour éviter les modifications involontaires et prolonger les temporaires
void afficher(const std::string& msg) {
std::cout << msg;
}
const std::string& ref = std::string("temporaire"); // durée de vie prolongée jusqu'à la fin de la portée
afficher(ref);
Combiner avec std::optional pour une liaison sécurisée
#include <optional>
std::optional<int> trouverValeur(bool ok) {
if (ok) return 42;
return std::nullopt;
}
void traitement() {
auto opt = trouverValeur(true);
if (opt) {
const auto& val = *opt; // référence sécurisée
std::cout << val;
}
}
Boucles for basées sur intervalle avec liaiosns structurées
std::map<std::string, int> notes = {{"Alice", 95}, {"Bob", 87}};
for (const auto& [nom, note] : notes) {
std::cout << nom << " : " << note << "\n";
}
Toujours utiliser auto& ou const auto& pour éviter les copies inutiles.
Règles d’or :
- Préférer les valeurs par défaut pour les temporaires.
- Vérifier que l’objet source survit à l’utilisation de ses références.
- Utiliser des outils d’analyse statique (clang-tidy, cppcheck) pour détecter les références pendantes.