GCC C++11 对用户定义的常量和模板参数包的限制

gcc c++11 limits for user defined constants and template parameter packs

本文关键字:参数 常量 C++11 用户 定义 GCC      更新时间:2023-10-16

我一直在gcc 4.7.2中使用用户定义的常量,并遇到了某种我不太理解的大小限制因素。

这个想法是为定点十进制类型定义一个 constexpr 运算符 "。我想避免从双重转换,而是使用可变参数模板在编译时解析尾数和指数。事实证明,尾数解析有点棘手。

当我启用下面代码底部的 3 个禁用行中的任何一个时,gcc 会陷入无限循环并挂在那里。我注意到浮点文字和可变参数模板的显式实例化的最大大小相同,但整数文字的最大大小略大。

我使用了命令:g++ -std=c++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 中的错误还是我错过了什么?

我必须说你发现了一些很好的角落情况,让编译器变得疯狂:-)对我来说,gcc 4.7.2 和 4.8 在编译过程中崩溃了。然而,clang(顶级版本)编译了整个代码,但使用了2.4GB RAM。该问题似乎与"."检查的三元运算符有关。如果您删除它并在 main() 中注释实数测试,一切都像爆炸一样编译良好。

因此,回答您的问题,您可能不会错过任何内容,gcc 和 clang 可能需要根据您的情况修改他们的实现。

根据 Mateusz 的回答,我使用 constexpr 函数重新定义了 literal_parser 模板来解析个位数,现在一切看起来都很完美。非常感谢您的帮助!

#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

永远不要将源文件放在末尾可能是个好主意,事实上,这在 Windows 下的 MinGW 下使用 g++ 4.7.2 编译得很好

g++ -std=c++11 -Wall -g literal_value.cpp -o literal_value
一般来说,最好

保留行的最后一部分用于链接规范,而不是其他任何内容。