编译器错误? g++ 允许可变大小的静态数组,除非函数是模板化的
Posted
技术标签:
【中文标题】编译器错误? g++ 允许可变大小的静态数组,除非函数是模板化的【英文标题】:Compiler bug? g++ allows variable-size static arrays, unless function is templated 【发布时间】:2012-06-06 23:14:54 【问题描述】:下面的代码演示了我无法解释的 gcc 4.6.2 的行为。第一个函数声明了一个 vec_t 类型的静态数组,其中 vec_t 是 unsigned char 的 typedef 别名。第二个函数是相同的,只是 vect_t 的类型是一个模板参数。第二个函数编译失败,诊断为“错误:‘bitVec’的存储大小不是常数”。
#include <limits>
void bitvec_func()
const std::size_t nbits = 1e7;
typedef unsigned char vec_t;
const std::size_t WLEN = std::numeric_limits<vec_t>::digits;
const std::size_t VSIZ = nbits/WLEN+1;
static vec_t bitVec[nbits/VSIZ]; // Compiles fine
template <typename T>
void bitvec_func()
const std::size_t nbits = 1e7;
typedef T vec_t;
const std::size_t WLEN = std::numeric_limits<vec_t>::digits;
const std::size_t VSIZ = nbits/WLEN+1;
static vec_t bitVec[nbits/VSIZ]; // "error: storage size of ‘bitVec’ isn’t constant"
void flarf()
bitvec_func();
bitvec_func<unsigned char>();
在我看来,使用参数
[附录:第二个函数将使用“-std=c++0x”或“-std=gnu++0x”编译,但我仍然想了解如何/如果在早期的语言定义下是错误的。]
预计到达时间: 如果 nbits 的初始值设定项改变,第二个函数将编译:
const std::size_t nbits = 1e7; // Error
const std::size_t nbits = (std::size_t)1e7; // Okay
const std::size_t nbits = 10000000.0; // Error
const std::size_t nbits = 10000000; // Okay
换句话说,如果nbits
是用整数类型的表达式初始化的,那么nbits
在bitVec
的定义中被视为常量。如果 nbits
改为使用浮点表达式初始化,则编译器不再将其视为 bitVec
维度的表达式中的常量,并且编译失败。
在 C++ 中调用“编译器错误”比在 C 中要舒服得多,但我想不出任何其他原因使上述 4 种情况在语义上不完全相同。还有人愿意发表意见吗?
【问题讨论】:
您能否发布导致编译器错误的确切代码?我似乎无法重现它。 这就是上面的确切代码。编译器是 gcc 4.6.2,选项是“-O0 -g3 -c”。 在较旧的 gcc 4.3.4 上,此代码 compiled fine. 有趣。我逐字复制了您的代码并在 4.6.2 上重现了该错误。也许这是一个相对较新的“功能” :) 感谢您的检查。 对不起,我之前没有意识到,但你想用nbits = 1e7;
做什么?如果您尝试将十六进制值分配给无符号整数,那将不起作用(另外,我认为这是问题所在)。
【参考方案1】:
在 gcc 4.7.0 上使用 -ansi
编译您的代码后,我能够重现此警告:
warning: ISO C++ forbids variable length array 'bitVec' [-Wvla]
both bitVec
出现此警告,而不仅仅是模板函数中的警告。然后我意识到nbits = 1e7;
行将double
分配给unsigned int
。我认为正因为如此,由于某种原因导致nbits
不是一个常量表达式。您的代码为非模板版本编译的原因是因为 gcc 的可变长度数组扩展。此外,出于某种原因,您的 gcc 版本不允许在函数模板中使用可变长度数组。要修复您的代码,请将 1e7;
更改为 10000000
。
编辑
我向another question询问了规则。答案是在 C++03 中代码无效,但在 C++11 中是可以的。
【讨论】:
它并没有丢失它的const
属性,而是(隐含的)constexpr
属性。
@MooingDuck:确实如此(已更正)。但是,我仍然想知道为什么会这样。
1e7
和 0x1e7
是非常不同的数字。 10000000
会是更好的替代品。
如上所述,1e7 (10,000,000) 是预期的。这似乎是问题的症结所在。 IANAL,但我的理解是 int i = 123.4
会将浮点常量转换为 int 并使用结果来初始化 i
。但是,将“nbits
”初始值设定项从1e7
更改为10000000
甚至(std::size_t)1e7
可以消除错误。
@JohnA:请参阅我的答案中的链接。在 C++03 中,int i = 123.4
无效,但在 C++11 中规则改变了,没关系。【参考方案2】:
答案当然是在产生错误的编译阶段,数组索引的存储大小不是恒定的——即它位于模板扩展的上游。动态数组不是 C++ 98/03 的一部分,它们是 gcc 扩展(最初是 C)。所以这个错误实际上是正确的,即使实现看起来很奇怪。据推测,GCC 会为模板化数组找到符合标准的路径,在需要时支持它们,否则会抛出错误,但静态类型的数组会到达“C”路径,因此会自动选择 gcc 扩展。
【讨论】:
代码实际上没有变量数组,所以错误是错误。数组的大小在编译时是已知的,并且应该由符合标准的编译器来处理。 这不仅仅是运行时可变性的问题。我相信(但我懒得检查),C++03 不允许在数组大小初始化器中专门使用模板参数(正是因为在正确的时间扩展它们的鸡与蛋问题)。毫无疑问,对规范有更深入了解的人会来纠正我。 有趣。这个(混乱)有效:template <typename T> void a() typedef T vec_t; static vec_t bitVec[sizeof(vec_t)*8];
以上是关于编译器错误? g++ 允许可变大小的静态数组,除非函数是模板化的的主要内容,如果未能解决你的问题,请参考以下文章