C++17 crée une utilisation pratique de l'opérateur d'indexation de tableau rétrograde, par Raymond Chen, Senior Software Engineer chez Microsoft

Il est bien connu que si a est un pointeur ou un tableau et i un entier, alors a[i] et i[a] sont équivalents en C et C++, ce qui donne lieu à des hilarités telles que :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
void haha()
{
    int a[5];
    for (i = 0; i < 5; i++) {
        i[a] = 42;
    }
}
Il y a très peu d'utilisation pratique pour cette équivalence, à part pour faire des farces aux gens¹.

Et puis C++17 est arrivé.

L'un des changements apportés au langage de base en C++17 a été le renforcement de l'ordre des règles d'évaluation, formellement connu sous le nom de séquençage. Nous avions déjà rencontré ce problème en étudiant un crash qui semblait concerner une opération std::move.

L'une des opérations qui a reçu un ordre d'évaluation défini est l'opérateur d'indice. À partir de C++17, a[b] évalue toujours a avant d'évaluer b.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
int* p;
int index();
 
auto test()
{
    return p[index()];
}
 
// Compiled as C++14
 
    sub     rsp, 40
    call    index       ; call index first
    movsxd  rcx, rax
    mov     rax, p      ; then fetch p
    mov     eax, [rax + rcx * 4]
    add     rsp, 40
    ret
 
// Compiled as c++17
 
    push    rbx
    sub     rsp, 32
    mov     rbx, p      ; fetch p first
    call    index       ; then call index
    movsxd  rcx, rax
    mov     eax, [rbx + rcx * 4]
    add     rsp, 32
    pop     rbx
    ret
Par conséquent, si votre évaluation de l'index peut avoir un effet secondaire sur l'évaluation du pointeur, vous pouvez inverser l'ordre pour forcer l'index à être calculé en premier.

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
auto test()
{
    return index()[p];
}
Étonnez vos amis ! Déconcertez vos ennemis !

Bavardage en prime : clang implémente cela correctement, mais msvc (v19) et gcc (v13) se trompent d'ordre et chargent p avant d'appeler index. (Par comparaison, icc se trompe aussi, mais dans l'autre sens : Il charge toujours p en dernier).

¹ Une autre utilisation pratique est de contourner toute surcharge possible de l'opérateur [], comme indiqué dans le chapitre 14 de Imperfect C++ :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
#define ARRAYSIZE(a) (sizeof(a) / sizeof(0[a]))
En inversant l'ordre dans 0[a], cela permet de contourner une éventuelle surcharge de a[].

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
std::vector<int> v(5);
int size = ARRAYSIZE(v); // compiler error
Cependant, cette méthode n'est pas infaillible. Il suffit de créer un imbécile plus astucieux : Si v est un pointeur ou un objet convertible en pointeur, alors ce pointeur ira volontiers à l'intérieur de 0[...].

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
3
4
5
6
7
8
9
10
11
12
struct Funny
{
    operator int*() { return oops; }
    int oops[5];
    int extra;
};
 
Funny f;
int size1 = ARRAYSIZE(f); // oops: 6
 
int* p = f;
int size2 = ARRAYSIZE(p); // oops: 1
Heureusement, vous n'avez pas besoin d'astuces macro. Vous pouvez laisser les fonctions constexpr du C++ faire le travail à votre place :

Code : Sélectionner tout - Visualiser dans une fenêtre à part
1
2
template<typename T, std::size_t N>
constexpr std::size_t array_size(T(&)[N]) { return N; }
Source : Raymond Chen, Microsoft

Et vous ?

Qu'en pensez-vous ?

Voir aussi :

Les travaux sur la norme C++ 23 sont terminés et cette nouvelle version porte le nom de code "Pandemic Edition", C++ 23 cherche à améliorer la vitesse de compilation et l'hygiène du code

Zig, présenté comme une alternative moderne au C, fait son apparition dans le top 50 de l'indice Tiobe des langages les plus populaires. Carbon, l'alternative C++ de Google, n'est classé que 168e

C++ vs Rust : une comparaison pratique de la vitesse de compilation et de test des deux langages de programmation, par Matthew Glazar, ingénieur en génie logiciel