Salut à tous ! L’article d’aujourd’hui porte sur une fonctionnalité de C++17 : les variables inline.
La P0386 a étendu l’utilisation du mot-clef inline aux variables membres statiques et aux variables globales. Comme pour les fonctions, une variable inline peut donc être définie dans des unités de traduction différentes. En effet, il s’agit du but premier du mot-clef inline pour les fonctions, l’indice d’optimisation sur l’inlining pour le compilateur n’est qu’un bonus (et le compilateur n’a aucune obligation). Dans ce cas, l’éditeur de liens choisira à son gré l’une des définitions (si les définitions sont différentes, il s’agit d’un undefined behavior).
Avant tout cela, la définition de variables statiques nécessitait de séparer la déclaration de la définition, de manière à respecter l’ODR :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
// mylib.h namespace ml { extern const int i; class MyClass { static std::string s; }; } // mylib.cpp const int ml::i = 42; std::string ml::MyClass::s = "Hello"; |
Bien entendu, il existait déjà des solutions partielles. On peut par exemple utiliser une fonction inline qui retourne le paramètre :
1 2 3 4 5 6 |
class MyClass { std::string& s() { static std::string s_; return s_; } }; |
Dans ce cas, en plus d’être plus verbeux à l’utilisation en raison de la syntaxe avec l’appel de fonction, le comportement est légèrement différent puisque s_ ne sera construite qu’à l’appel de la fonction (ce qui pour des types plus complexes peut être un problème).
L’arrivée de constexpr a permis de simplifier la chose :
1 2 3 |
class MyClass { static constexpr auto i = 12; }; |
Bien pratique n’est-ce pas ? Le souci étant que tout ne se plie pas aux règles de constexpr et ce n’est donc pas utilisable dans le cas général. De plus, si i est odr-used, il est tout de même nécessaire de définir i en dehors de la classe :
1 2 3 4 5 6 |
class MyClass { static constexpr auto i = 12; }; constexpr int i; // définition en dehors de la classe. const int* p = &MyClass::i; // MyClass::i est odr-used. |
C’est pourquoi C++17 propose les variables inline, qui peuvent donc être définies de multiples fois :
1 2 3 |
class MyClass { static inline std::string s = "Hello"; }; |
Dans le cas d’une variable globale à la portée d’un namespace par exemple, on a ici la garantie que l’adresse de la variable (ou de la constante) sera la même dans toutes les unités de traduction. Sémantiquement, cela revient à une variable extern dont la définition n’a pas être placée dans une seule unité de traduction puisque les variables globales inline ont un external linkage.
A l’inverse des fonctions, une variable membre définie dans la définition d’une classe n’est pas automatiquement inline, mais doit être explicitement déclarée comme tel. Cette décision a certainement vocation à ne pas interférer avec le code existant.
Les variables membres statiques constexpr sont implicitement inline. Il est toujours possible de faire une définition en dehors de la classe mais cela est d’ores et déjà déprécié et sera potentiellement retiré du standard par la suite.
Merci à tous de m’avoir lu, et je vous dis à bientôt !
You must log in to post a comment.