J'ai constexpr std::array<int, N> v1{0}, v2{0}; qui se comporte comme de grands entiers. J'écris donc une fonction multiply pour trouver le produit des nombres.

#include <array>
#include <cstdint>
using namespace std;

template <int N>
constexpr array<int, 2 * N> multiply(const array<int, N> &a,
                                     const array<int, N> &b) {
    const int as = N, bs = N, rs = as + bs;
    array<int, rs> result{0};

    __int128_t carry = 0;
    auto pr = begin(result);
    for (int r = 0, lim = min(rs, as + bs - 1); r < lim; ++r) {
        int i = r >= as ? as - 1 : r,           
            j = r - i,
            k = i < bs - j ? i + 1 : bs - j;    // min(i+1, bs-j);
        auto pa = begin(a) + i;
        auto pb = begin(b) + j;
        while (k--) {
            carry += static_cast<__int128_t>(*(pa--)) * (*(pb++));
        }
        *(pr++) = static_cast<int64_t>(carry);
    }
    return result;
}

int main() {
    constexpr int N = 20;
    constexpr array<int, N> v1{0}, v2{0};
    constexpr array<int, 2 *N> result = multiply<N>(v1, v2);
    return result[1];
}

Notez que la fonction de multiplication est incorrecte pour la rendre minimale.

Lorsque je compile ce code à l'aide de clang++ -std=c++17 -Wall -O0 example.cc, j'obtiens par erreur :

example.cc:30:32: error: constexpr variable 'result' must be initialized by a constant expression
    constexpr array<int, 2 *N> result = multiply<N>(v1, v2);
                               ^        ~~~~~~~~~~~~~~~~~~~
example.cc:20:50: note: cannot refer to element -1 of array of 20 elements in a constant expression
            carry += static_cast<__int128_t>(*(pa--)) * (*(pb++));
                                                 ^
example.cc:30:41: note: in call to 'multiply(v1, v2)'
    constexpr array<int, 2 *N> result = multiply<N>(v1, v2);
                                        ^
1 error generated.

Mais cela se compile correctement avec gcc.

Pourquoi je pense que l'erreur de clang est une erreur :

Pour vérifier s'il existe un accès hors limite, j'ai activé le mode de débogage de libstdc++ et compilé à l'aide de g++ -std=c++17 -Wall -D_GLIBCXX_DEBUG -g -O0 example.cc et il n'y a eu aucun plantage qui se serait produit s'il y avait eu un accès hors limite.

Également sous désinfectants (g++ -fsanitize=address,undefined -fno-omit-frame-pointer) le code s'est exécuté avec succès.

Je suis curieux de savoir pourquoi clang revendique un accès hors limite, alors que les expériences montrent clairement que ce n'est pas le cas.

-1
madhur4127 25 janv. 2020 à 11:32

1 réponse

Meilleure réponse

Clang a raison. Vous n'avez pas "erreur obtenu" l'erreur.

*(pa--)

En supposant que k est initialement défini sur i + 1, avant la dernière fois que cette expression est évaluée dans la boucle while, pa pointe vers le premier élément du déployer. pa-- implique l'évaluation de pa - 1, ce qui entraîne un comportement indéfini selon [expr.add]/4 : (i = 0, j = 1)

Lorsqu'une expression de type intégral est ajoutée ou soustraite à un pointeur, le résultat a le type de l'opérande du pointeur. Si l'expression P pointe vers l'élément x[i] d'un objet tableau x avec des éléments n, les expressions P + J et J + P (où J a la valeur j) pointe vers l'élément (éventuellement hypothétique) x[i + j] si 0 ≤ i + j ≤ n ; sinon, le comportement n'est pas défini. De même, l'expression P - J pointe vers l'élément (éventuellement hypothétique) x[i − j] si 0 ≤ i − j ≤ n ; sinon, le comportement n'est pas défini.

Désormais, une expression constante ne doit pas évaluer : [expr.const]/2.6

une opération qui aurait un comportement indéfini comme spécifié dans les articles [intro] à [cpp] de la présente Norme internationale

Par conséquent, multiply<N>(v1, v2) n'est pas une expression constante et un diagnostic est requis ici.

Bien sûr, ce pointeur [-1] ne posera généralement pas de problèmes à moins que vous ne le déréférenciez, mais il s'agit néanmoins d'un comportement indéfini, qui l'empêche de faire partie d'une expression constante. Les désinfectants ne peuvent diagnostiquer qu'un sous-ensemble limité de comportements indéfinis.

2
L. F. 25 janv. 2020 à 11:06