GCC C++11 对用户定义的常量和模板参数包的限制
gcc c++11 limits for user defined constants and template parameter packs
我一直在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
一般来说,最好保留行的最后一部分用于链接规范,而不是其他任何内容。
- 如何创建长度由常量参数指定的数组
- 通过常量引用传递参数的矩阵模板类
- 具有常量引用参数的函数模板专用化
- 使用自动推导的 lambda 参数作为常量表达式
- C++:常量引用参数
- 常量参数"real"常量吗?
- 常量参数存储在哪里 (C++)?
- 常量函数,当其参数是对文字类型的引用时
- 区分接受常量参数的函数引用/指针和与函数参数同名的非常量参数
- 必须非常量别名参数及其默认参数常量
- 字符串参数常量字符* 和常量 wchar_t*
- 可选参数常量引用重新分配
- 推导模板化类参数的模板参数:常量问题
- 从函数参数常量字符串 (&) 设置值
- 为什么我必须声明这些引用参数常量或按值传递
- C++使用一个参数常量重载
- 如果要执行const_cast,为什么要制作参数常量?
- 模板非类型参数常量限制筛选器库
- 标记方法指针/引用参数常量真的会显著影响性能吗
- 当函数参数常量引用 T 时,为什么 T 的模板参数推导'skips'数组元素的恒定性?