clang ++与g ++ constexpr的差异,带有超出范围的引用

问题描述 投票:0回答:1

我有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.

但是这可以用gcc正确编译。

为什么我认为clang的错误是一个错误:

为了验证是否存在访问权限超出限制,我启用了libstdc ++的调试模式,并使用g++ -std=c++17 -Wall -D_GLIBCXX_DEBUG -g -O0 example.cc进行了编译,如果访问权限超出限制,则不会发生崩溃。

同样在消毒剂(g++ -fsanitize=address,undefined -fno-omit-frame-pointer)下运行成功。

我很好奇为什么clang声称超出了访问范围,而实验清楚地表明并非如此。

c++ g++ c++17 constexpr clang++
1个回答
0
投票

C是对的。您没有“ erroneously get”错误。

*(pa--)

假设k最初设置为i + 1,在最后一次在while循环中最后一次对该表达式求值之前,pa指向数组的第一个元素。 pa--涉及评估pa - 1,根据[expr.add]/4会导致未定义的行为:(i = 0j = 1

当将具有整数类型的表达式添加到指针或从指针中减去时,结果将具有指针操作数的类型。如果表达式P指向具有x[i]元素的数组对象x的元素n,则表达式P + JJ + P(其中J的值为j)指向(如果x[i + j],则可能是假设的)元素0 ≤ i + j ≤ n;否则,行为是不确定的。 [同样,如果P - J,则表达式x[i − j]指向(可能是假设的)元素0 ≤ i − j ≤ n;否则,行为是不确定的。

现在,常量表达式不能求值:[expr.const]/2.6

将具有本国际标准第[intro]至[cpp]节中指定的不确定行为的操作

因此,multiply<N>(v1, v2)不是常数表达式,并且在这里需要诊断。

当然,除非您取消引用它,否则[-1]指针通常不会引起问题,但是它仍然是未定义的行为,这阻止了它成为常量表达式的一部分。消毒剂只能诊断出未定义行为的有限子集。

© www.soinside.com 2019 - 2024. All rights reserved.