超大数值字面量的类型选择规则与编译器合规性问询
我测试了GCC、clang、MSVC三大编译器的行为,对哪种符合C++标准存在疑问,以下是测试代码:
// p43.cxx #include <type_traits> constinit auto n1 = 9223372036854775807; // 2^63 - 1 static_assert(std::is_same_v<decltype(n1), signed long long int>); constinit auto n2 = 9223372036854775808; // 2^63 constinit auto n3 = 18446744073709551615; // 2^64 - 1 #if defined _MSC_VER || defined __clang__ static_assert(std::is_same_v<decltype(n2), unsigned long long int>); static_assert(std::is_same_v<decltype(n3), unsigned long long int>); #elif defined __GNUC__ static_assert(std::is_same_v<decltype(n2), __int128>); static_assert(std::is_same_v<decltype(n3), __int128>); #endif #if defined __GNUC__ && !defined __clang__ constinit auto n4 = 18446744073709551616; // 2^64 static_assert(std::is_same_v<decltype(n4), int>); #endif
执行以下语法检查命令均无错误:
g++ -std=c++2c p43.cxx -Wall -Wextra -Wpedantic -fsyntax-only clang++ -std=c++2c p43.cxx -Wall -Wextra -Wpedantic -fsyntax-only cl /nologo /std:c++latest /nologo /Wall /wd4577 p43.cxx /Zs
(cl编译时禁用C4577以简化输出)
根据C++标准草案:
[lex.icon]/3 十进制字面量应优先选择int、long int或long long int类型;
[lex.icon]/4 若上述类型无法容纳字面量,允许使用扩展整数类型(如__int128)。
测试结果
- 所有编译器均判定
n1的类型为signed long long int,符合预期。 - 对于
n2和n3:- MSVC无警告,将二者类型判定为
unsigned long long int; - clang提示“integer literal is too large to be represented in a signed integer type, interpreting as unsigned”,同样判定为
unsigned long long int; - GCC给出类似警告,但选择了有符号扩展类型
__int128。
- MSVC无警告,将二者类型判定为
- 仅GCC支持定义
n4,但将其类型判定为int;MSVC和clang会直接报错提示数值过大。
疑问解答
若不支持signed __int128,9223372036854775808和18446744073709551615是否应判定为语法错误?
根据C++标准,十进制字面量属于有符号类型范畴,当标准有符号整数类型(int/long/long long)无法容纳时,必须优先使用支持的扩展有符号整数类型;若不存在能容纳该值的扩展有符号类型,这个字面量属于无效的无类型字面量,应判定为语法错误。clang和MSVC将其转为无符号字面量的行为属于编译器扩展,不符合标准的严格规定,但属于常见的兼容处理逻辑。GCC默认支持__int128,但为何将18446744073709551616解析为int类型,而非能容纳它的signed __int128?
这是GCC的非标准行为。按照标准,该数值可以被signed __int128容纳,理应选择此类型,但GCC在处理超过unsigned long long范围的十进制字面量时,会触发整数溢出回绕逻辑,将其截断为int类型,这属于编译器历史遗留的非标准处理方式,不符合C++标准的要求。
(补充:测试平台为PC Windows x86-64 LLP64)
内容的提问来源于stack exchange,提问作者Sventikas




