[英]clang++ vs g++ constexpr difference with out of bound reference
我有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 的錯誤是一個錯誤:
為了驗證是否存在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
編譯,並且沒有發生崩潰,如果有越界訪問。
同樣在消毒劑下( g++ -fsanitize=address,undefined -fno-omit-frame-pointer
)代碼成功運行。
我很好奇為什么 clang 聲稱越界訪問,而實驗清楚地表明情況並非如此。
叮當是對的。 你沒有“錯誤地得到”錯誤。
*(pa--)
假設k
最初設置為i + 1
,在最后一次在while
循環中計算此表達式之前, pa
指向數組的第一個元素。 pa--
涉及評估pa - 1
,根據[expr.add]/4導致未定義的行為:( i = 0
, j = 1
)
當具有整數類型的表達式與指針相加或相減時,結果具有指針操作數的類型。 如果表達式
P
指向具有n
元素的數組對象x
元素x[i]
,則表達式P + J
和J + P
(其中J
的值為j
)指向(可能是假設的)元素x[i + j]
如果0 ≤ i + j ≤ n
; 否則,行為未定義。 同樣,如果0 ≤ i − j ≤ n
,則表達式P - J
指向(可能是假設的)元素x[i − j]
; 否則,行為未定義。
現在,常量表達式不能求值: [expr.const]/2.6
具有本國際標准的 [intro] 至 [cpp] 條款中規定的未定義行為的操作
因此, multiply<N>(v1, v2)
不是常量表達式,這里需要進行診斷。
當然,這個[-1]
指針通常不會引起問題,除非您取消引用它,但它仍然是未定義的行為,這阻止了它成為常量表達式的一部分。 消毒劑只能診斷未定義行為的有限子集。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.