Вопрос или проблема
Я собрал такую программу в Ubuntu 18.04 с g++ 8.4.0, и опции компиляции -fno-strict-overflow -fwrapv
.
#include <iostream>
#include <string>
namespace _crypt {
// clang-format off
// начальное значение времени компиляции
#define XSTR_SEED ((__TIME__[7] - '0') * 1ull + (__TIME__[6] - '0') * 10ull + \
(__TIME__[4] - '0') * 60ull + (__TIME__[3] - '0') * 600ull + \
(__TIME__[1] - '0') * 3600ull + (__TIME__[0] - '0') * 36000ull)
// -----------------------------------------------------------------------------
// @return псевдослучайное число, ограниченное значением 0xFFFFFFFF
constexpr unsigned long long linear_congruent_generator(unsigned rounds) {
return 1013904223ull + (1664525ull * ((rounds> 0) ? linear_congruent_generator(rounds - 1) : (XSTR_SEED) )) % 0xFFFFFFFF;
}
// -----------------------------------------------------------------------------
#define Random() linear_congruent_generator(10)
#define XSTR_RANDOM_NUMBER(Min, Max) (Min + (Random() % (Max - Min + 1)))
// -----------------------------------------------------------------------------
constexpr const unsigned long long XORKEY = XSTR_RANDOM_NUMBER(0, 0xFF);
// -----------------------------------------------------------------------------
template <typename Char>
constexpr Char encrypt_character(const Char character, int index) {
return character ^ (static_cast<Char>(XORKEY) + index);
}
template <unsigned size, typename Char>
class Xor_string {
public:
const unsigned _nb_chars = (size - 1);
Char _string[size];
// если все пройдет хорошо, этот конструктор должен быть выполнен во время компиляции
inline constexpr Xor_string(const Char* string) : _string{} {
for (unsigned i = 0; i < size; ++i) _string[i] = encrypt_character(string[i], i);
}
// Это выполняется во время выполнения.
// ХАК: хотя decrypt() является const, мы модифицируем '_string' на месте
const Char* decrypt() const {
Char* string = const_cast<Char*>(_string);
for (unsigned t = 0; t < _nb_chars; t++) {
string[t] = string[t] ^ (static_cast<Char>(XORKEY) + t);
}
string[_nb_chars] = '\0';
return string;
}
};
} // namespace _crypt
//#ifdef _XOR_STINRG_
#define XorS(name, my_string) constexpr _crypt::Xor_string<(sizeof(my_string)/sizeof(char)), char> name(my_string)
#define XorString(my_string) []{constexpr _crypt::Xor_string<(sizeof(my_string)/sizeof(char)), char> expr(my_string); return expr; }().decrypt()
// Шифрование обычной строки char*
#define _c( string ) XorString( string )
#define XorWS(name, my_string) constexpr _crypt::Xor_string<(sizeof(my_string)/sizeof(wchar_t)), wchar_t> name(my_string)
#define XorWideString(my_string) []{ constexpr _crypt::Xor_string<(sizeof(my_string)/sizeof(wchar_t)), wchar_t> expr(my_string); return expr; }().decrypt()
// _crypt широкие символы
#define _cw( string ) XorWideString( string )
int main() {
static std::string test_string = _c(R"(
fdaslkjdsfafjaksdljfa;lksdflajljffffffffffffffffasdfklasjdfkljaskdljfkl;asjdf;aljdsf;kljasdklfjklsadjfklasdjkl;fjasdkl;dfj;lkasdjf;lkasdjf;lkjasd;klfjldsjf;kljasdklfjklsadjfklasdjkl
sdjf;lkasjdfkl;ajsd;lkfjaskl;djf;klasjdf;lkajsdlkfjal;sdkjfkl;asdjrfioqwejhotpiqhntg;lsadnvclmkasdhjdo;fijasd;lkfja;wlkjetpoiquropiqwejrlsjfaadlkjfoiqawhghsngb;lkasewriopqweujtophkjzdnvlkzxdnlkfahsjeftoipwqhytpoiqwhngfkjsafbgfnkljasjf;laksddjfopiqaweehtgoasnfd;laksddjfopiqaweehtgoasnfdldsfj;alskeeurtpoiqwe
kdjsf;lokasudtrfiopqweujtpoiqu;lqwekngsadndfl;ksadjvb;lkasujropiwqehjrtpoiwqethgl;knf;lkasvnv;lkanss;dletrjwioqeurpoqweujrpowqejtojsdfa;ljf
)");
std::cout << test_string <<'\n';
return 0;
}
Я встретил такую проблему:
в 'constexpr' расширении '_crypt::Xor_string<636, char>(((const char*)" \012 fdaslkjdsfafjaksdljfa;lksdflajljffffffffffffffffasdfklasjdfkljaskdljfkl;asjdf;aljdsf;kljasdklfjklsadjfklasdjkl;fjasdkl;dfj;lkasdjf;lkasdjf;lkjasd;klfjldsjf;kljasdklfjklsadjfklasdjkl\012 sdjf;lkasjdfkl;ajsd;lkfjaskl;djf;klasjdf;lkajsdlkfjal;sdkjfkl;asdjrfioqwejhotpiqhntg;lsadnvclmkasdhjdo;fijasd;lkfja;wlkjetpoiquropiqwejrlsjfaadlkjfoiqawhghsngb;lkasewriopqweujtophkjzdnvlkzxdnlkfahsjeftoipwqhytpoiqwhngfkjsafbgfnkljasjf;laksddjfopiqaweehtgoasnfd;laksddjfopiqaweehtgoasnfdldsfj;alskeeurtpoiqwe\012 kdjsf;lokasudtrfiopqweujtpoiqu;lqwekngsadndfl;ksadjvb;lkasujropiwqehjrtpoiwqethgl;knf;lkasvnv;lkanss;dletrjwioqeurpoqweujrpowqejtojsdfa;ljf\012"))'
/home/gx/Project/selflearning/cpp17_complete_guide/src/template/test.hpp:40:71: в 'constexpr' расширении '_crypt::encrypt_character<char>(((int)(*(string + ((sizetype)i)))), ((int)i))'
/home/gx/Project/selflearning/cpp17_complete_guide/src/template/test.hpp:59:116: ошибка: переполнение в константном выражении [-fpermissive]
t::Xor_string<(sizeof(my_string)/sizeof(char)), char> expr(my_string); return expr; }().decrypt()
Когда я читаю информацию об ошибке, я вижу, что тип шаблона Char
был выведен как int
:
_crypt::encrypt_character<char>(((int)(*(string + ((sizetype)i)))), ((int)i))
Однако, когда я удаляю опции компиляции -fno-strict-overflow -fwrapv
, эта проблема исчезает. Я не знаю, почему и как справиться с этой проблемой. Более того, когда я использую g++-9.4.0 и опции компиляции -fno-strict-overflow -fwrapv
, я не встречаю эту проблему! Я хочу решить эту проблему и узнать, почему я столкнулся с ней в g++-8.4.0, спасибо!
Я пробовал переписать этот код следующим образом:
template <typename Char>
constexpr int encrypt_character(const Char character, int index) {
return character ^ (static_cast<Char>(XORKEY) + index);
}
Я использую int
вместо Char
, и вышеупомянутая проблема исчезла!
Ответ или решение
Ошибка g++-8.4.0: overflow in constant expression [-fpermissive]
возникает из-за особенностей обработки константных выражений в версии компилятора g++ 8.4.0. Давайте внимательно разберем причины, которые могут привести к данной ошибке, и изучим способы ее обхода.
Природа проблемы
Ваш код использует шаблонные функции и constexpr, что позволяет проводить вычисления на этапе компиляции. Ошибка, с которой вы столкнулись, связана с вычислениями внутри функции encrypt_character
, где тип Char
неявно преобразуется к типу int
. В результате, при выполнении арифметических операций может произойти переполнение при вычислении выражения:
character ^ (static_cast<Char>(XORKEY) + index);
Это объясняет, почему когда вы компилируете без флагов -fno-strict-overflow
и -fwrapv
, проблема исчезает. Эти флаги заставляют компилятор учитывать поведение арифметических операций с учетом переполнения и других особенностей, таких как модульная арифметика.
Детерминация типа Char
Вы также заметили, что тип Char
в вашем encrypt_character
был неявно преобразован к int
. Это подтверждает, что значения, используемые в арифметических операциях, не всегда ведут себя так, как вы ожидаете, особенно когда вы работаете с разными типами.
При использовании g++ 9.4.0 улучшилась обработка константных выражений, что могло решить данную проблему. Новый компилятор может справляться с переполнением в некоторых случаях лучше, чем предыдущая версия.
Решение проблемы
Чтобы избежать данной ошибки, вы можете внести изменения в определение функции encrypt_character
. Например, обернуть выражение в static_cast<int>
:
template<typename Char>
constexpr int encrypt_character(const Char character, int index) {
return static_cast<int>(character) ^ (static_cast<int>(XORKEY) + index);
}
Это изменение заставляет компилятор производить вычисления в диапазоне, который не приводит к переполнению. Использование int
вместо Char
также облегчает выполнение арифметических операций и предотвращает возможные ошибки, связанные с типами.
Подведение итогов
- Ошибка
overflow in constant expression
в g++ 8.4.0 вызвана особенностями обработки типов и арифметических операций в constexpr-функциях. - Использование флагов компиляции, таких как
-fno-strict-overflow
, может влиять на поведение компилятора, но не всегда решает проблему. - Обновление на более новые версии компилятора, такие как g++ 9.4.0, может устранить ошибки.
- Рекомендованное решение заключается в явном указании типов, чтобы избежать неявного сведения типов.
Понимание основ работы компилятора с типами и выражениями позволит вам более эффективно разрабатывать и отлаживать свои программы.