Насколько мне известно, данные-члены окружающего класса также видны во вложенном классе.
struct A {
struct B {
int arr[n]; // how come n is not visible here, but it is visible in next statement.
int x = n;
};
static const int n = 5;
};
Потому что int x = n;
— это полный контекст класса.
Интересный; int arr[A::n];
компилируется с последней версией msvc
, но не с gcc
.
@JasonLiam, что это значит? но если мы объявим n перед запуском вложенного класса, он будет работать нормально.
@ 273K, почему мы не можем объявить arr[n] ?
@mn_op Если бы вы объявили n
перед вложенным классом, поиск имени мог бы найти n
, и это работает. Но когда вы объявляете его после вложенного класса, поиск имени не может его найти, а также он не находится в полном контексте класса.
@JasonLiam OP, вероятно, имел в виду: почему n
не определено при использовании в определении массива, в то время как оно определено при назначении x. В обоих случаях static const int n = 5;
определяется после его вызова.
@RohanBari int a = n;
работает, потому что это полный контекст класса, и стандарт С++ это позволяет. С другой стороны, int arr[n];
не является полным контекстом класса, и стандарт С++ не позволяет этого.
@JasonLiam, когда мы используем полное имя для a, оно работает на msvc. [godbolt.org/z/5MTPTh8Ks] смотрите здесь.
@mn_op Похоже, это ошибка компилятора.
@mn_op Полный контекст класса (который применяется к инициализатору по умолчанию, но не к типу нестатического члена) означает, что поиск имени выполняется из него, как если бы окончание }
класса уже было достигнуто. Так что тогда не имеет значения, как упорядочена декларация n
.
@mn_op Вот отчет об ошибке msvc
почему n не видно здесь, но видно в следующем утверждении.
Потому что int x = n;
— это полный контекст класса, а int arr[n];
— нет. Это можно понять из class.mem, в котором говорится:
6) Полноклассовый контекст класса — это:
6.4) инициализатор члена по умолчанию
в пределах спецификации члена класса. [Примечание: контекст полного класса вложенного класса также является контекстом полного класса любого охватывающего класса, если вложенный класс определен в спецификации члена охватывающего класса. - заключительное примечание ]
7) Класс считается полностью определенным типом объекта ([basic.types]) (или полным типом) в конце } спецификатора класса. Класс считается полным в контексте его полного класса; в противном случае он считается неполным в рамках своей собственной спецификации члена класса.
(выделено мной)
Обратите внимание, что n
внутри int arr[n];
является частью типа массива, но приведенный выше оператор не позволяет использовать n
как часть типа нестатического члена данных и, следовательно, ошибка. По той же причине мы получим ту же ошибку, если напишем:
struct A {
struct B {
int x = n;
//----------------------v--------->same error as before
std::array<int, n> arr;
};
static constexpr int n = 5;
};
Цитируемая часть стандарта, по-видимому, объясняет, что такое «контекст полного класса», но не объясняет, почему быть или не быть контекстом полного класса означает n
быть видимым или нет в том или ином выражении. Следует также #7 цитировать для полноты картины? В частности, класс считается полным в контексте его полного класса?
@Enlico Добавил ваше предложение для полноты (каламбур)
@JasonLiam, тогда почему мы можем использовать n
как часть типа массива в теле функции-члена? [godbolt.org/z/jGnqEj99e] смотрите демо здесь.
@mn_op Тело функции также является контекстом полного класса: timsong-cpp.github.io/cppwp/n4861/class.mem#6.1
Кое-что о переменной оценки в левой части задания, а не эксперте по компьютерному языку, сработало следующее.
#include <iostream>
using namespace std;
struct A {
struct B {
int *arr = new int[n]();
int x = n;
};
static const int n = 5;
};
int main() {
cout << "Hello World!";
return 0;
}
Это не отвечает на вопрос. ОП не ищет обходного пути.
Мой ответ больше похож на интуицию, почему это так:
В int arr[n]
n используется для определения поля, а в int x = n
n используется для инициализации поля. Это как B() : x(n) {}
.
Подумайте о следующем примере:
struct A {
struct B {
int arr[n];
int x = n;
};
static constexpr int n = offsetof(B, x);
};
Здесь значение n
зависит от размера arr
, а размер обрр зависит от n
. Это перекрестная зависимость. Следовательно, struct
не совсем точно определен.
Если бы правила С++ были таковы, что ваш вариант использования был бы законным, то это также должно быть законным, чего не может быть. Тем не менее, он по-прежнему позволяет нам использовать offsetof
при инициализации, а почему бы и нет?
Что ты спрашиваешь?