g++-8.4.0 ошибка: переполнение в константном выражении [-fpermissive]

Вопрос или проблема

Я собрал такую программу в 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, может устранить ошибки.
  • Рекомендованное решение заключается в явном указании типов, чтобы избежать неявного сведения типов.

Понимание основ работы компилятора с типами и выражениями позволит вам более эффективно разрабатывать и отлаживать свои программы.

Оцените материал
Добавить комментарий

Капча загружается...