声明与 const 变量和成员函数相同的标识符

Declare the same identifier as const variable and member function

我写了下面这段代码

#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。编译器如何解析变量的名称声明?

在成员函数的范围内(即使是内联定义的),class 被认为是完整的1。因此,使用 N 必须使用成员枚举器。它的值必须是 3.

但是,声明 class 成员数据时情况并非如此。此时(指定 array 时),class 未被视为完整。所以N只能引用之前看到的,也就是说必须是全局常量。

Clang 接受它,但发出 6 (sizeof(int) * 5 / 3)。 GCC (8) 没有,但它并不是真正的无效代码。这只是容易出错。更好定义的方法是将枚举器移动到数组定义之前

enum { N = 3 };
int array[N];

...或者如果我们不这样做,那么我们可以使用范围解析来引用 "correct N"

sizeof(array) / ::N

重新排列 class 定义会更好,因为它不会仍然容易出错(我们可以忘记使用合格的 ::N)。


1: 来自最新的C++标准草案

[class.mem]/6

A complete-class context of a class is a

  • function body ([dcl.fct.def.general]),
  • default argument ([dcl.fct.default]),
  • noexcept-specifier,
  • contract condition ([dcl.attr.contract]), or
  • default member initializer

within the member-specification of the class.

在行

int array[N];

N 是全局 N.

在函数foo()中,N是在enum中定义的N

foo()的定义里面,class的定义是用来解析名字的。但是,在成员变量的声明中,只有该行之前的声明用于解析名称。

如果您将 class 更改为

class X
{
   public:
      enum
      {
         N = 3    
      };

      int array[N];
      void foo()
      {
         std::cout << "array size:"<<sizeof(array)/N << std::endl;   
      }
};

那么,用来定义arrayN就是enum中定义的N

PS 这对于理解语言很有用,但请永远不要在现实世界的应用程序中使用这种编码风格。

问题来自声明int array[N];

根据[basic.scope.class]/2

A name N used in a class S shall refer to the same declaration in its context and when re-evaluated in the completed scope of S. No diagnostic is required for a violation of this rule.

在声明的上下文中,N 解析为引用 ::N,但在 X 的完整范围内(所有成员现在可见),N 解析为引用枚举数 X::N,因此程序格式错误;不需要诊断。