【发布时间】:2020-01-25 08:32:55
【问题描述】:
我有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 进行编译,如果存在越界访问,则不会发生崩溃。
同样在 sanitizers (g++ -fsanitize=address,undefined -fno-omit-frame-pointer) 代码下运行成功。
我很好奇为什么 clang 声称越界访问,而实验清楚地表明事实并非如此。
【问题讨论】:
-
为什么是 clang++ -std=c++17 -Wall -O0 ? ..try -O3
-
没有崩溃不代表程序有效。 UB 只是未定义。什么事情都可能发生。 (我不是说有UB,我只是说没有崩溃不代表没有)。
-
@super,使用
-D_GLIBCXX_DEBUG编译会处理越界访问,而 Sanitizers 会处理未定义的行为。 -
clang 不是在抱怨越界 access,它是在抱怨创建指向数组外部的指针(它不在乎你没有取消引用它)。
-
@MarcGlisse,如果属实,那么问题就大了,因为我不应该为我没有做的事情买单!警告就够了,但错误太多了!
标签: c++ c++17 undefined-behavior constexpr clang++