Nouvelles Du Monde

C++23 : les petites perles du langage de base

C++23 : les petites perles du langage de base

2023-07-31 09:16:00

Il y a plus dans le langage de base C++23 que de déduire cela. Aujourd’hui, je vais écrire sur les petites perles.

Publicité


Rainer Grimm travaille depuis de nombreuses années en tant qu’architecte logiciel, chef d’équipe et responsable de la formation. Il aime écrire des articles sur les langages de programmation C++, Python et Haskell, mais aime aussi intervenir fréquemment lors de conférences spécialisées. Sur son blog Modernes C++, il traite intensément de sa passion pour le C++.



C++23 fournit de nouveaux suffixes littéraux intégraux pour (signé) std::size_t un.



std :: taille_t (C++17) est un type de données non signé qui peut contenir la taille maximale de n’importe quel type. Il est souvent utilisé pour indexer des tableaux et compter dans des boucles.

Un exemple d’utilisation consiste à parcourir un vecteur. Pour des raisons d’optimisation, sa taille est stockée dans le cache.

Publicité

#include 

int main() {

    std::vector v{0, 1, 2, 3};
    for (auto i = 0, s = v.size(); i < s; ++i) {
	    /* use both i and v[i] */
    }
}

Lors de la compilation, le message d'erreur suivant apparaît Explorateur de compilateur:



la raison en est auto i dans int et s dans long unsigned int dérivé. Par conséquent, le problème n'est pas résolu même si les deux variables ne sont pas signées.

#include 

int main() {

    std::vector v{0, 1, 2, 3};
    for (auto i = 0u, s = v.size(); i < s; ++i) {
	    /* use both i and v[i] */
    }
}

Maintenant, le compilateur dirige i pour unsigned intmais s pour long unsigned int loin. La capture d'écran suivante montre à nouveau la sortie d'erreur de l'explorateur du compilateur.



C++23 corrige ce problème avec le nouveau suffixe littéral z.

#include 

int main() {

    std::vector v{0, 1, 2, 3};
    for (auto i = 0uz, s = v.size(); i < s; ++i) {
	    /* use both i and v[i] */
    }
}

Cet exemple est basé sur la proposition P0330R8. Il comprend des exemples plus motivants des nouveaux suffixes littéraux.

if consteval se comporte comme if (std::is_constant_evaluated()) { }mais présente quelques avantages :

  1. Il n'y aura pas d'en-tête nécessaire.
  2. Il a une syntaxe plus simple que std::is_constant_evaluated.
  3. Il peut être utilisé pour exécuter des fonctions immédiates (consteval les fonctions).

std::is_constant_evaluated est une fonction C++20 qui détecte si un constexpr La fonction est exécutée pendant la compilation.

Sur cppreference.com/is_constant_evaluated voici un excellent exemple :

#include 
#include 
#include 
 
constexpr double power(double b, int x)
{
    if (std::is_constant_evaluated() && !(b == 0.0 && x < 0))
    {
        // A constant-evaluation context: 
        // Use a constexpr-friendly algorithm.
        if (x == 0)
            return 1.0;
        double r {1.0};
        double p {x > 0 ? b : 1.0 / b};
        for (auto u = unsigned(x > 0 ? x : -x); u != 0; u /= 2)
        {
            if (u & 1)
                r *= p;
            p *= p;
        }
        return r;
    }
    else
    {
        // Let the code generator figure it out.
        return std::pow(b, double(x));
    }
}
 
int main()
{
    // A constant-expression context
    constexpr double kilo = power(10.0, 3);
    int n = 3;
    // Not a constant expression, because n cannot be
    // converted to an rvalue
    // in a constant-expression context
    // Equivalent to std::pow(10.0, double(n))
    double mucho = power(10.0, n);
 
    std::cout << kilo << " " << mucho << "n"; // (3)
}

La fonction power est constexpr. Cela signifie qu'il peut être exécuté au moment de la compilation. Le premier appel de power provoque l'exécution à la compilation car le résultat est demandé à la compilation : constexpr double kilo = power(10.0, 1). Le deuxième appel, en revanche, ne peut être exécuté qu'à l'exécution car l'argument de la fonction n n'est pas une expression constante : double mucho = power(10.0, n).

Humide std::is_constant_evaluated un code différent est exécuté à la compilation et à l'exécution. Au moment de la compilation, le if-Exécuté et à l'exécution la branche else-Bifurquer. Les deux power Les vues totalisent 1000.

Une fonction immédiate est une consteval-Fonction. Un consteval-Function est une fonction qui ne peut être exécutée qu'au moment de la compilation. Plus à propos consteval-Functions est dans mon post C++20 : Deux nouveaux mots clés en C++20 : consteval et constinit.

Basé sur consteval if laisse toi std::is_constant_evaluated implémenter:

constexpr bool is_constant_evaluated() {
    if consteval {
        return true;
    } else {
        return false;
    }
}

Un moyen générique d'obtenir une copie d'un objet en C++ est auto copy = x;. Cela fonctionne mais a un problème: copy est une lvalue, mais parfois vous voulez une prvalue. prvalue est l'abréviation de pure rvalue. Une rvalue pure est une expression dont l'évaluation initialise un objet. Pour en savoir plus sur les catégories de valeur, consultez le post de Barry : Catégories de valeurs en C++17.

Les vues auto(x) et auto{x} convertir x en prvalue comme s'ils x serait passé comme argument de fonction par valeur. auto(x) et auto{x} conduire un copie de décomposition à travers.

Dans mes cours de formation, on me demande souvent ce que signifie la décomposition. Je voudrais donc l'expliquer plus en détail. Decay signifie essentiellement que certaines informations de type sont perdues lors de la copie d'une valeur. Un exemple typique est une fonction qui prend son argument comme valeur. Voici les différents types de caries :

  1. Conversion de tableau en pointeur
  2. Conversion de fonction en pointeur
  3. Jeter de const/volatile Qualificatif
  4. Suppression de références

Le programme suivant montre les quatre types de désintégrations :

// decay.cpp

void decay(int*, void(*)(int), int, int ) { }      // (5)

void func(int){}                                   // (2)

int main() {

    int intArray[5]{1, 2, 3, 4, 5};                // (1)
    const int myInt{5};                            // (3)
    const int& myIntRef = myInt;                   // (4)

    decay(intArray, func, myInt, myIntRef);       

}

La fonction decay (5) a besoin d'un pointeur vers un intun pointeur de fonction et deux ints. Le premier argument de l'appel de fonction est un int-Array(1), le deuxième une fonction(2), le troisième une const int (3) et le dernier est un const int& (4).

La bibliothèque de traits de type a la fonction std :: pourriture. Avec cette fonction, vous pouvez le faire carie appliquer directement à un type. En conséquence, ce sont les conversions de type correspondantes avec std::decay.

// decayType.cpp

#include 

int main() {
     
    // (1)
    // int[5] -> int* 
    static_assert(std::is_same::type, 
                  int*>::value);             
    
    // (2)
    // void(int) -> void(*)(int)
    static_assert(std::is_same::type,
                  void(*)(int)>::value);  
    
    // (3)
    // const int -> int
    static_assert(std::is_same::type, 
                  int>::value);           
    
    // (4)
    // const int& -> int
    static_assert(std::is_same::type, 
                  int>::value);          

}

Il existe d'autres petits joyaux en C++23. Dans mon prochain article, je poursuivrai mon voyage avec plus de fonctionnalités de langage de base de C++23.


(rme)

Vers la page d'accueil



#C23 #les #petites #perles #langage #base
1690949347

Facebook
Twitter
LinkedIn
Pinterest

Leave a Comment

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

ADVERTISEMENT