Подтвердить что ты не робот

Gcc С++ 11 ограничений для пользовательских констант и пакетов параметров шаблона

Я играл с определенными пользователем константами в gcc 4.7.2 и сталкивался с некоторыми факторами ограничения размера, которые я не совсем понимаю.

Идея заключалась в том, чтобы определить оператор constexpr "" для десятичного типа с фиксированной точкой. Я хочу, чтобы избежать кастинга из двойного, а скорее манифеста синтаксического анализа и экспоненты во время компиляции с использованием вариативных шаблонов. Парсинг мантиссы оказался немного сложным.

Когда я включаю любую из 3 отключенных строк внизу кода ниже, gcc попадает в бесконечный цикл и висит там. Я заметил такой же максимальный размер для литерала с плавающей запятой и явного создания шаблона вариации, но немного большего размера для целочисленного литерала.

Я использовал команду: g++ -std = С++ 11 -Wall -g -o literal_value literal_value.cpp

Использование -ftemplate-depth-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 или я чего-то не хватает?

4b9b3361

Ответ 1

Я должен сказать, что вы нашли приятный угловой случай, который делает компиляторы сумасшедшими:-) Для меня gcc 4.7.2 и 4.8 разбился во время компиляции. Однако clang (верхняя версия) скомпилировал весь код в порядке, но использовал 2,4 ГБ оперативной памяти. Кажется, что проблема связана с тернатным оператором для '.' проверить. Если вы удалите его и прокомментируете реальные числовые тесты в main(), все компилируется как взрыв.

Таким образом, отвечая на ваш вопрос, вы, вероятно, ничего не пропустите, и gcc и clang должны, вероятно, пересмотреть свою реализацию на основе вашего дела.

Ответ 2

На основе ответа Mateusz я переопределил шаблон 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;
}

Ответ 3

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.

Ответ 4

Я думаю, что проблема заключается в том, как вы составляете свою команду

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

В общем случае неплохо зарезервировать последнюю часть строки для привязки спецификаций и ничего больше.