clang ++ vs g ++ constexpr разница с отсутствующей ссылкой - PullRequest
0 голосов
/ 25 января 2020

У меня есть constexpr std::array<int, N> v1{0}, v2{0};, который ведет себя как большие целые числа. Итак, я пишу multiply функцию, чтобы найти произведение чисел.

#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];
}

Обратите внимание, что функция умножения неверна, чтобы сделать ее минимальной.

Когда я компилирую этот код, используя clang++ -std=c++17 -Wall -O0 example.cc, я ошибочно получаем:

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.

Но это правильно компилируется с g cc.

Почему я думаю, что ошибка Clang это ошибка:

Чтобы проверить, есть ли доступ за пределами привязки, я включил режим отладки в libstdc ++ и скомпилировал с использованием g++ -std=c++17 -Wall -D_GLIBCXX_DEBUG -g -O0 example.cc, и не было cra sh, что могло бы произойти, если бы был выход за пределы access.

Также под дезинфицирующим средством (g++ -fsanitize=address,undefined -fno-omit-frame-pointer) код был успешно выполнен.

Мне любопытно, почему clang утверждает, что он не имеет связанного доступа, хотя эксперименты ясно показывают, что это не так.

1 Ответ

2 голосов
/ 25 января 2020

Clang прав. Вы не " ошибочно получите " ошибку.

*(pa--)

Предполагая, что k изначально установлен на i + 1 до того, как в последний раз В последний раз это выражение вычисляется в while l oop, pa указывает на первый элемент массива. pa-- включает оценку pa - 1, что приводит к неопределенному поведению в соответствии с [expr.add] / 4 : (i = 0, j = 1)

Когда выражение, имеющее целочисленный тип, добавляется или вычитается из указателя, результат имеет тип операнда указателя. Если выражение P указывает на элемент x[i] объекта массива x с n элементами, выражения P + J и J + P (где J имеет значение j) указывают на ( возможно-гипотетический) элемент x[i + j] if 0 ≤ i + j ≤ n; в противном случае поведение не определено. Аналогично, выражение P - J указывает на (возможно, гипотетический) элемент x[i − j] if 0 ≤ i − j ≤ n; в противном случае поведение не определено.

Теперь константное выражение не должно вычисляться: [expr.const] /2.6

операция, которая будет иметь неопределенное поведение, как указано в пунктах [intro] - [cpp] настоящего международного стандарта

Следовательно, multiply<N>(v1, v2) не является константным выражением, и требуется диагностика c здесь.

Конечно, этот указатель [-1] обычно не вызовет проблем, если вы не разыграете его, но, тем не менее, это неопределенное поведение, которое не позволяет ему быть частью константного выражения. Дезинфицирующие средства могут диагностировать только ограниченное подмножество неопределенного поведения.

...