[英]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 elementx[i]
of an array objectx
withn
elements, the expressionsP + J
andJ + P
(whereJ
has the valuej
) point to the (possibly-hypothetical) elementx[i + j]
if0 ≤ i + j ≤ n
;如果表达式
P
指向具有n
元素的数组对象x
元素x[i]
,则表达式P + J
和J + 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) elementx[i − j]
if0 ≤ 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.