
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 |
1 2 3 4 5 6 7 | void haha() { int a[5]; for (i = 0; i < 5; i++) { i[a] = 42; } } |
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 |
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 |
Code : | Sélectionner tout |
1 2 3 4 | auto test() { return index()[p]; } |
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 |
#define ARRAYSIZE(a) (sizeof(a) / sizeof(0[a]))
Code : | Sélectionner tout |
1 2 | std::vector<int> v(5); int size = ARRAYSIZE(v); // compiler error |
Code : | Sélectionner tout |
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 |
Code : | Sélectionner tout |
1 2 | template<typename T, std::size_t N> constexpr std::size_t array_size(T(&)[N]) { return N; } |
Et vous ?

Voir aussi :



Vous avez lu gratuitement 3 articles depuis plus d'un an.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.
Soutenez le club developpez.com en souscrivant un abonnement pour que nous puissions continuer à vous proposer des publications.