簡體   English   中英

clang++ vs g++ constexpr 與越界引用的區別

[英]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 + JJ + 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM