简体   繁体   English

clang++ vs g++ constexpr 与越界引用的区别

[英]clang++ vs g++ constexpr difference with out of bound reference

I have constexpr std::array<int, N> v1{0}, v2{0};我有constexpr std::array<int, N> v1{0}, v2{0}; which behaves like big integers.它的行为类似于大整数。 So, I write a multiply function to find the product of the numbers.所以,我写了一个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];
}

Note that the multiply function is incorrect to make it minimal.请注意,乘法函数不正确以使其最小。

When I compile this code using clang++ -std=c++17 -Wall -O0 example.cc , I erroneously get:当我使用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.

But this compiles correctly with gcc.但这可以用 gcc 正确编译。

Why I think clang's error is an error:为什么我认为 clang 的错误是一个错误:

To verify whether there is an out of bound access I enabled libstdc++'s debug mode and compiled using g++ -std=c++17 -Wall -D_GLIBCXX_DEBUG -g -O0 example.cc and there was no crash which would have happened if there was an out of bound access.为了验证是否存在g++ -std=c++17 -Wall -D_GLIBCXX_DEBUG -g -O0 example.cc访问,我启用了 libstdc++ 的调试模式并使用g++ -std=c++17 -Wall -D_GLIBCXX_DEBUG -g -O0 example.cc编译,并且没有发生崩溃,如果有越界访问。

Also under sanitizers ( g++ -fsanitize=address,undefined -fno-omit-frame-pointer ) code ran successfully.同样在消毒剂下( g++ -fsanitize=address,undefined -fno-omit-frame-pointer )代码成功运行。

I am curious as to why clang claims out of bound access, while the experiments clearly shows it is not the case.我很好奇为什么 clang 声称越界访问,而实验清楚地表明情况并非如此。

Clang is right.叮当是对的。 You did not " erroneously get " the error.你没有“错误地得到”错误。

*(pa--)

Assuming that k is initially set to i + 1 , prior to the last time the last time this expression is evaluated in the while loop, pa points to the first element of the array.假设k最初设置为i + 1 ,在最后一次在while循环中计算此表达式之前, pa指向数组的第一个元素。 pa-- involves evaluating pa - 1 , which results in undefined behavior according to [expr.add]/4 : ( i = 0 , j = 1 ) pa--涉及评估pa - 1 ,根据[expr.add]/4导致未定义的行为:( i = 0 , j = 1 )

When an expression that has integral type is added to or subtracted from a pointer, the result has the type of the pointer operand.当具有整数类型的表达式与指针相加或相减时,结果具有指针操作数的类型。 If the expression P points to element x[i] of an array object x with n elements, the expressions P + J and J + P (where J has the value j ) point to the (possibly-hypothetical) element x[i + j] if 0 ≤ i + j ≤ n ;如果表达式P指向具有n元素的数组对象x元素x[i] ,则表达式P + JJ + P (其中J的值为j )指向(可能是假设的)元素x[i + j]如果0 ≤ i + j ≤ n otherwise, the behavior is undefined.否则,行为未定义。 Likewise, the expression P - J points to the (possibly-hypothetical) element x[i − j] if 0 ≤ i − j ≤ n ;同样,如果0 ≤ i − j ≤ n ,则表达式P - J指向(可能是假设的)元素x[i − j] otherwise, the behavior is undefined.否则,行为未定义。

Now, a constant expression must not evaluate: [expr.const]/2.6现在,常量表达式不能求值: [expr.const]/2.6

an operation that would have undefined behavior as specified in Clauses [intro] through [cpp] of this International Standard具有本国际标准的 [intro] 至 [cpp] 条款中规定的未定义行为的操作

Therefore, multiply<N>(v1, v2) is not a constant expression, and a diagnostic is required here.因此, multiply<N>(v1, v2)不是常量表达式,这里需要进行诊断。

Of course, this [-1] pointer will usually not cause problems unless you dereference it, but it's nonetheless undefined behavior, which prevents it from being a part of a constant expression.当然,这个[-1]指针通常不会引起问题,除非您取消引用它,但它仍然是未定义的行为,这阻止了它成为常量表达式的一部分。 Sanitizers can only diagnose a limited subset of undefined behavior.消毒剂只能诊断未定义行为的有限子集。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM