2024-01-15 13:47:00
Aujourd’hui, je voudrais parler des fonctionnalités moins connues std::span
et écrivez les dangers. Il s’agit également d’une préoccupation personnelle pour la recherche sur la SLA.
Publicité
Rainer Grimm travaille depuis de nombreuses années en tant qu’architecte logiciel, responsable d’équipe et de formation. Il aime écrire des articles sur les langages de programmation C++, Python et Haskell, mais aime également intervenir fréquemment lors de conférences spécialisées. Sur son blog Modern C++, il parle intensément de sa passion C++.
UN std::span
, parfois appelé vue, n’est jamais propriétaire. Cette mémoire contiguë peut être un simple tableau, un pointeur d’une certaine taille std::array,
un std::vector
ou un std::string
être. Une implémentation typique consiste en un pointeur vers son premier élément et une taille. La principale raison d’un std::span
est : Un tableau C est réduit à un pointeur vers son premier élément lorsqu’il est passé à une fonction. Cela entraîne une perte de la taille du tableau. Ce pourriture est une raison typique des erreurs en C/C++.
Taille déterminée automatiquement
En revanche, dirige std::span
automatiquement la taille des séquences d’objets connectées.
// printSpan.cpp
#include
#include
#include
#include
void printMe(std::span container) {
std::cout << "container.size(): " << container.size() << 'n'; // (4)
for(auto e : container) std::cout << e << ' ';
std::cout << "nn";
}
int main() {
std::cout << std::endl;
int arr[]{1, 2, 3, 4}; // (1)
printMe(arr);
std::vector vec{1, 2, 3, 4, 5}; // (2)
printMe(vec);
std::array arr2{1, 2, 3, 4, 5, 6}; // (3)
printMe(arr2);
}
Le tableau C (1), std::vector
(2) et que std::array
(3) posséder int
s. Contient par conséquent std::span
aussi int
s. Il y a autre chose d’intéressant dans cet exemple simple. Pour chaque conteneur peut std::span
dériver sa taille (4).
C'était un petit rappel std::span
. Tout le reste se trouve dans mon article précédent "C++20 : Accès protégé aux séquences d'objets avec std::span".
Un std::span peut avoir une étendue statique ou une étendue dynamique.
Etendue statique ou dynamique
Par défaut, il a std::span
une étendue dynamique :
template
class span;
Lorsqu'un std::span
a une étendue statique, sa taille est connue au moment de la compilation et fait partie du type de données : std::span
. Par conséquent, leur implémentation n’a besoin que d’un pointeur vers le premier élément de la séquence d’objets contiguë.
La mise en œuvre d'un std::span
avec une étendue dynamique se compose d'un pointeur vers le premier élément et de la taille de la séquence contiguë d'objets. La taille ne fait pas partie du std::span
-taper.
L'exemple suivant illustre les différences entre les deux types de travées.
// staticDynamicExtentSpan.cpp
#include
#include
#include
void printMe(std::span container) { // (3)
std::cout << "container.size(): " << container.size() << 'n';
for (auto e : container) std::cout << e << ' ';
std::cout << "nn";
}
int main() {
std::cout << 'n';
std::vector myVec1{1, 2, 3, 4, 5};
std::vector myVec2{6, 7, 8, 9};
std::span dynamicSpan(myVec1); // (1)
std::span staticSpan(myVec2); // (2)
printMe(dynamicSpan);
printMe(staticSpan);
// staticSpan = dynamicSpan; ERROR // (4)
dynamicSpan = staticSpan; // (5)
printMe(staticSpan); // (6)
std::cout << 'n';
}
dynamicSpan
(1) a une étendue dynamique, tandis que staticSpan
(2) en a un statique. Les deux std::span
s donnent leur taille dans la fonction printMe
(3) retour. UN std::span
avec une étendue statique, peut-on std::span
peut être attribué avec une étendue dynamique, mais pas l'inverse. (4) provoquerait une erreur, mais (5) et (6) sont valides.
Il existe un cas d'utilisation particulier de std::span
. Un std::span
peut être une gamme constante d’éléments modifiables.
Une gamme constante d'éléments modifiables
Pour plus de commodité, je vais en nommer un std::vector
et une std::span
Zone. UN std::vector
modélise une gamme mutable d'éléments mutables : std::vector
. Si tu as ça std::vector
comme const
déclaré, il modélise une plage constante d'objets constants : const std::vector
. Vous ne pouvez pas modéliser une plage constante d'éléments modifiables. À ce stade vient std::span
dans le jeu. UN std::span
modélise une gamme constante d'objets mutables : std::span
. La figure suivante illustre les différences entre les zones (constantes/modifiables) et les éléments (constants/modifiables).
// constRangeModifiableElements.cpp
#include
#include
#include
void printMe(std::span container) {
std::cout << "container.size(): " << container.size() << 'n';
for (auto e : container) std::cout << e << ' ';
std::cout << "nn";
}
int main() {
std::cout << 'n';
std::vector origVec{1, 2, 2, 4, 5};
// Modifiable range of modifiable elements
std::vector dynamVec = origVec; // (1)
dynamVec[2] = 3;
dynamVec.push_back(6);
printMe(dynamVec);
// Constant range of constant elements
const std::vector constVec = origVec; // (2)
// constVec[2] = 3; ERROR
// constVec.push_back(6); ERROR
std::span constSpan(origVec); // (3)
// constSpan[2] = 3; ERROR
// Constant range of modifiable elements
std::span dynamSpan{origVec}; // (4)
dynamSpan[2] = 3;
printMe(dynamSpan);
std::cout << 'n';
}
Le vecteur dynamVec
(1) est une zone modifiable avec des éléments modifiables. Cette déclaration ne s'applique pas au vecteur constVec
(2). constVec
ne peut pas changer ses éléments ni sa taille. constSpan
(3) se comporte en conséquence. dynamSpan
(4) modélise le cas d'utilisation unique d'une région constante avec des éléments variables.
Enfin, je voudrais souligner deux dangers lors de l'utilisation std::span
devrait savoir.
Dangers de std :: span
Les problèmes typiques de std::span
sont deux choses différentes. Premièrement, il faut std::span
ne pas être appliqué à une zone temporaire ; deuxièmement, la taille de la zone contiguë sous-jacente doit être d'un std::span
ne soit pas modifié.
UN std::spa
n vers une zone temporaire
UN std::span
n'est jamais propriétaire et ne prolonge pas la durée de vie de la zone sous-jacente. Il faudrait donc std::span
ne fonctionnent que sur les lValeurs. L'utilisation d'un std::span
sur une zone temporaire est un comportement indéfini.
// temporarySpan.cpp
#include
#include
#include
std::vector getVector() { // (2)
return {1, 2, 3, 4, 5};
}
int main() {
std::cout << 'n';
std::vector myVec{1, 2, 3, 4, 5}; // (1)
std::span mySpan1{myVec};
std::span mySpan2{getVector().begin(), 5}; // (3)
for (auto v: std::span{myVec}) std::cout << v << " ";
std::cout << 'n';
for (auto v: std::span{getVector().begin(), 5}) std::cout << v << " "; // (4)
std::cout << "nn";
}
L'utilisation d'un std::span
avec une étendue statique ou un std::span
avec une étendue dynamique sur la lValue, c'est bien. Si je viens de lValue std::vector
en (1) à un temporaire std::vector
changez cela via la fonction getVector
(2) est généré, le programme a un comportement indéfini. (3) et (4) sont tous deux invalides. Une fois exécuté, le comportement non défini devient visible. Le résultat de (4) n'est pas d'accord avec cela std::vector
correspondre à celui de la fonction getVector()
est produit.
Modification de la taille de la région contiguë sous-jacente
La modification de la taille de la zone contiguë sous-jacente peut entraîner la réaffectation de la zone contiguë et la std::span
fait référence à des données obsolètes.
std::vector myVec{1, 2, 3, 4, 5};
std::span sp1{myVec};
myVec.push_back(6); // undefined behavior
Et après?
Dans mon prochain article, je me replongerai dans la bibliothèque de formatage C++20.
Notre campagne de financement pour la recherche sur la SLA
Mon pack de 3 livres Collection C++ moderne est maintenant disponible à moitié prix (70 $ -> 35 $). Je donnerai cet argent à la recherche sur la SLA. Permettez-moi de commencer par les faits.
La recherche sur la SLA est gravement sous-financée
Voici une explication du Clinique externe SLA:
La SLA est l’un des « enfants à problèmes » de la médecine – les maladies rares ne sont pas au centre de l’attention sociale. Il n’existe pas de financement général pour la recherche sur la SLA. En conséquence, le traitement des personnes atteintes de SLA est considérablement sous-financé. Les dons et le soutien extérieur sont ici très importants.
Ce sous-financement est dû au Défi du seau de glace devenir clair.
La recherche a besoin de plus d’argent. Voici mon idée. Commençons une collecte de fonds !
Notre campagne de financement
Le coupon pour le lot de 3 livres Collection C++ moderne à moitié prix (70$ -> 35$) est valable jusqu'au 21 janvier (inclus).
Les trois livres suivants sont inclus dans ce pack : La bibliothèque standard C++, Concurrence avec le C++ moderne et C++20.
Je donnerai tout l’argent à la clinique externe de SLA. La clinique externe SLA fait partie de la Charité et est leader dans la recherche et le traitement de la SLA en Europe.
(moi)
#Développement #logiciel #stdspan #C20 #détails
1705328845