Я играл с пользовательскими константами в gcc 4.7.2 и столкнулся с какими-то факторами, ограничивающими размер, которые я не совсем понимаю.
Идея заключалась в том, чтобы определить оператор constexpr «» для десятичного типа с фиксированной запятой. Я хочу избегать приведения типа double, а лучше разбирать мантиссу и экспоненту во время компиляции с использованием шаблонов с переменными числами. Разбор мантиссы оказался немного хитрым.
Когда я включаю любую из 3 отключенных строк внизу кода ниже, gcc попадает в бесконечный цикл и зависает там. Я заметил тот же максимальный размер для литерала с плавающей запятой и явного создания экземпляра шаблона с переменными числами, но немного больший размер для целочисленного литерала.
Я использовал команду: g ++ -std = c ++ 11 -Wall -g -o literal_value literal_value.cpp
Использование -ftemplate-глубина-128 не имеет значения.
#include <iostream>
#include <cstdint>
typedef std::uint64_t value_type;
template<value_type Temp, char... List> struct literal_parser;
template<value_type Temp, char Head, char... List>
struct literal_parser<Temp, Head, List...>
{
static const value_type value = Head == '.' ?
literal_parser<Temp, List...>::value :
literal_parser<Temp * 10 + Head - '0', List...>::value;
};
template<value_type Temp, char Last>
struct literal_parser<Temp, Last>
{
static const value_type value = Last == '.' ?
Temp : Temp * 10 + Last - '0';
};
template<char... List>
inline constexpr value_type operator"" _value() noexcept
{
return literal_parser<0U, List...>::value;
}
int main()
{
std::cout << "value 1: " << 123456789012345678_value << std::endl;
std::cout << "value 2: " << 1.23456789012345_value << std::endl;
std::cout << "value 3: " << literal_parser<0U, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'>::value << std::endl;
#if 0
std::cout << "value 4: " << 1234567890123456789_value << std::endl;
std::cout << "value 5: " << 1.234567890123456_value << std::endl;
std::cout << "value 6: " << literal_parser<0U, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6'>::value << std::endl;
#endif
}
Это ошибка в gcc или я что-то упустил?
Я должен сказать, что вы нашли какой-то хороший случай, который сводит с ума компиляторы 🙂 Для меня gcc 4.7.2 и 4.8 потерпели крах во время компиляции. Однако clang (топ-версия) прекрасно скомпилировал весь код, но использовал 2,4 ГБ ОЗУ. Кажется, проблема связана с оператором ternaty для ‘.’ проверять. Если вы удалите его и прокомментируете тесты с действительными числами в main (), то все будет прекрасно, как взрыв.
Поэтому, отвечая на ваш вопрос, вы, вероятно, ничего не пропустите, и gcc и clang, вероятно, должны пересмотреть их реализацию в зависимости от вашего случая.
Основываясь на ответе Матеуша, я переопределил шаблон literal_parser, используя функцию constexpr для анализа одной цифры, и теперь все выглядит идеально. Большое спасибо за вашу помощь!
#include <iostream>
#include <cstdint>
typedef std::uint64_t value_type;
template<value_type Temp, char... List> struct literal_parser;
inline constexpr value_type parse_digit(value_type value, char digit) noexcept
{
return digit == '.' ? value : value * 10 + digit - '0';
}
template<value_type Temp, char Head, char... List>
struct literal_parser<Temp, Head, List...>
{
static const value_type value =
literal_parser<parse_digit(Temp, Head), List...>::value;
};
template<value_type Temp, char Last>
struct literal_parser<Temp, Last>
{
static const value_type value = parse_digit(Temp, Last);
};
template<char... List>
inline constexpr value_type operator"" _value() noexcept
{
return literal_parser<0U, List...>::value;
}
int main()
{
std::cout << "value 1: " << 123456789012345678_value << std::endl;
std::cout << "value 2: " << 1.23456789012345_value << std::endl;
std::cout << "value 3: " << literal_parser<0U, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5'>::value << std::endl;
std::cout << "value 4: " << 1234567890123456789_value << std::endl;
std::cout << "value 5: " << 1.2345678901234567890_value << std::endl;
std::cout << "value 6: " << literal_parser<0U, '1', '2', '3', '4', '5', '6', '7', '8', '9', '0', '1', '2', '3', '4', '5', '6'>::value << std::endl;
}
static const value_type value = Head == '.' ?
literal_parser<Temp, List...>::value :
literal_parser<Temp * 10 + Head - '0', List...>::value;
Это заставит время компиляции полностью взорваться, потому что компилятор должен оценить обе стороны условного выражения (делая все это экспоненциальным по количеству цифр). Попробуйте изменить выражение на что-то вроде literal_parser<Head == '.' ? Temp : Temp * 10 + Head - '0', List...>::value
,
Я думаю, что проблема заключается в том, как вы составляете свою команду
g++ -std=c++11 -Wall -g -o literal_value literal_value.cpp
Вероятно, это хорошая идея — никогда не ставить исходные файлы в конец, ведь это прекрасно компилируется с g ++ 4.7.2 под MinGW под Windows
g++ -std=c++11 -Wall -g literal_value.cpp -o literal_value
в общем, неплохо бы зарезервировать последнюю часть строки для связи спецификаций и ничего больше.