Я написал следующий фрагмент кода

#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. Как компилятор разрешил объявление имен переменных?

4
getsoubl 21 Авг 2018 в 08:58

3 ответа

Лучший ответ

Проблема возникает из объявления int array[N];.

Согласно [basic.scope.class] / 2:

Имя N, используемое в классе S, должно относиться к тому же объявлению в его контексте и при повторной оценке в завершенной области действия S. Для нарушения этого правила не требуется никакой диагностики.

В контексте объявления N разрешается ссылаться на ::N, но в завершенной области видимости X (все члены теперь видны), N разрешается в обратитесь к перечислителю X::N, поэтому программа некорректна; Диагностика не требуется.

1
xskxzr 21 Авг 2018 в 08:06

Внутри области функций-членов (даже определенных внутри строк) класс считается завершенным 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 ++

< Сильный > [ class.mem ] / 6

Полный класс класса является

  • тело функции ([dcl.fct.def.general]),
  • аргумент по умолчанию ([dcl.fct.default]),
  • noexcept - спецификатор ,
  • условие договора ([dcl.attr.contract]), или
  • инициализатор элемента по умолчанию

в пределах спецификации члена класса.

6
Oliv 21 Авг 2018 в 07:08

В линии

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, используемый для определения array, является тем, который определен в th enum.

< Сильный > PS Это полезно для понимания языка, но, пожалуйста, никогда не используйте такой стиль кодирования в реальных приложениях.

2
R Sahu 21 Авг 2018 в 06:09
51942442