Nouvelles Du Monde

Développement logiciel : std::span en C++20 : Plus de détails

Développement logiciel : std::span en C++20 : Plus de détails

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++.

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 ints. Contient par conséquent std::span aussi ints. 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.

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::spans 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.

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.

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::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

Dans mon prochain article, je me replongerai dans la bibliothèque de formatage C++20.



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.

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 !

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)

Vers la page d'accueil



#Développement #logiciel #stdspan #C20 #détails
1705328845

Facebook
Twitter
LinkedIn
Pinterest

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.

ADVERTISEMENT