我写了下面的代码
#include <iostream>
const int N = 5;
class X
{
public:
int array[N];
void foo()
{
std::cout << "array size:"<<sizeof(array)/N << std::endl;
}
enum
{
N = 3
};
};
int main()
{
X x;
x.foo();
}
上述代码无法在GCC中编译:
<source>:13:8: error: declaration of 'N' [-fpermissive]
N = 3
^
<source>:2:11: error: changes meaning of 'N' from 'const int N' [-fpermissive]
const int N = 5;
^
从我的观点出发,将数组定义为由五个整数组成的数组,将N定义为5。编译器如何解析变量的名称声明?
答案 0 :(得分:6)
在成员函数(甚至是内联定义的成员函数)的范围内,该类也被认为是完整的 1 。因此,使用N
必须使用成员枚举器。并且其值必须为3。
但是,声明类成员数据时并非如此。在这一点上(指定array
时),该类不被认为是完整的。因此N
只能引用以前看到的内容,这意味着它必须是全局常数。
Clang接受它,但发出6
(sizeof(int) * 5 / 3
)。 GCC(8)没有,但是它并不是真正的无效代码。只是容易出错。定义更好的方法是将枚举器移到定义数组之前
enum { N = 3 };
int array[N];
...否则,我们可以使用范围解析来引用“正确的N”
sizeof(array) / ::N
重新排列类定义会更好,因为它仍然不会容易出错(我们可以忘记使用合格的::N
)。
1:摘自最新的C ++标准草案
一个类的完整类上下文是
- 功能主体([dcl.fct.def.general]),
- 默认参数([dcl.fct.default]),
- noexcept-specifier
- 合同条件([dcl.attr.contract]),或
- 默认成员初始化程序
在类的成员规范内。
答案 1 :(得分:2)
在线
int array[N];
N
是全局N
。
在功能foo()
中,N
是enum
中定义的那个。
在foo()
的定义中,该类的定义用于解析名称。但是,在成员变量的声明中,仅使用该行之前的声明来解析名称。
如果您将课程更改为
class X
{
public:
enum
{
N = 3
};
int array[N];
void foo()
{
std::cout << "array size:"<<sizeof(array)/N << std::endl;
}
};
然后,用于定义N
的{{1}}是在array
中定义的那个。
PS 这对于理解语言很有用,但是请不要在实际应用中使用这种编码风格。
答案 2 :(得分:1)
问题来自声明int array[N];
。
在S类中使用的名称N在上下文中以及在S的完整范围中进行重新评估时应引用相同的声明。对于此规则的违反,无需进行诊断。
在声明的上下文中,N
被解析为引用::N
,但是在X
的完整范围(现在所有成员都可见)中,N
解析为引用枚举器X::N
,因此程序格式错误;无需诊断。