Порядок инициализации статической переменной класса

У меня есть класс A, который имеет две статические переменные. Я хотел бы инициализировать один с другой, несвязанной статической переменной, так же, как это:

#include <iostream>
class A
{
public:
    static int a;
    static int b;
};

int A::a = 200;
int a = 100;
int A::b = a;
int main(int argc, char* argv[])
{
    std::cout << A::b << std::endl;

    return 0;
}

Результат 200. Итак, может ли кто-нибудь сказать мне, почему?

Ответ 1

Это правильно в соответствии с правилами поиска. [basic.lookup.unqual]/13 говорит:

Имя, используемое в определении статического члена данных класса X (после квалифицированного идентификатора статического члена), выглядит так, как если бы имя использовалось в функции-члене X. [Примечание: [class.static.data ] далее описывает ограничения на использование имен в определении статического члена данных. - конечная нота]

Поскольку неквалифицированный a выглядит так, как будто вы находитесь внутри функции-члена, он должен сначала найти элемент A::a. Порядок инициализации A::a и A::b не влияет на поиск, хотя он влияет на то, насколько точно определяется результат.

Ответ 2

Так, может ли кто-нибудь сказать мне, почему?

Это четко указано в basic.scope.class/4, акцент мой:

Потенциальная область объявления, которая распространяется или заканчивается окончанием определения класса, также распространяется на регионы, определенные его определениями элементов, даже если члены определяются лексически вне класса (это включает определения статических данных, определения вложенных классов, и определения функций-членов, включая тело функции-члена и любую часть декларативной части таких определений, которая следует за идентификатором-декларатором, включая предложение-объявление-параметр и любые аргументы по умолчанию).

Таким образом, когда у вас есть

int A::a = 200;
int a = 100;
int A::b = a; // note the '::' scope resolution operator
              // OUTPUT: 200

a самом деле ссылается на A::a поскольку область видимости класса расширена на A::b.

В отличие от того, если у вас есть:

int A::a = 200;
int a = 100;
int b = a; // note b is not A::b
           // i.e. without the '::', scope resolution operator
           // OUTPUT: 100

a будет ссылаться на (глобальный) ::a так как b здесь не является членом class A,
т.е. расширение расширения класса.

Ответ 3

С++ проект /class.static

Если в определении статического члена, использующего идентификатор-член-член , используется неквалифицированный-id, а поиск имени ([basic.lookup.unqual]) находит, что unqualified-id относится к статическому члену, перечислителю или вложенному типу класса-члена (или базового класса класса-члена), неквалифицированный-id преобразуется в выражение с квалифицированным идентификатором, в котором спецификатор вложенных имен называет область класса, от которой ссылается элемент. [Примечание: см. [Expr.prim.id] для ограничения использования нестатических элементов данных и нестатических функций-членов. - конечная нота]

В нем говорится, что неквалифицированный идентификатор превращается в выражение с квалифицированным идентификатором в вашей ситуации.

int A::b = a;

Вы можете установить identified-id, но не иметь вложенного имени-спецификатора, подобного этому.

int A::b = ::a;

Ответ 4

Поскольку имя ищет вверх, решает a как A::a. Если вы хотите сделать это, вам нужно будет вручную решить эту область:

int A::b = ::a;
        // ^ Global scope resolution

Живой пример