Разница в видимости членов класса и обычных переменных

#include <iostream>

struct S {
    int m = 41;
    S(int m = 42): m{m} { std::cout << m << '\n'; }
};

int main() {
    int m = 41;
    { int m = m; std::cout << m << '\n'; } // (1) not 41
    S{};                                   // (2) 42
}

Почему в 1 случае не используется значение 41 внешней переменной, а во втором используется внешний параметр 42? Какие правила Стандарта определяют, что int m = m; и m{m} работают именно так?


Ответы (2 шт):

Автор решения: Harry

Во втором случае int m в пределах блока скрывает переменную int m вне его.

В первом - это инициализация, так что первое m относится к члену-данным структуры, а второе — локальная переменная (переданная как параметр).

А вот о конкретных местах стандарта — это надо искать...

→ Ссылка
Автор решения: wololo

Поиск имён для инициализируемых членов в списке инициализации конструктора не учитывает область видимости параметров конструктора.

class.base.init / 2:

mem-initializer:
  mem-initializer-id ( expression-listopt)
  mem-initializer-id braced-init-list

Lookup for an unqualified name in a mem-initializer-id ignores the constructor's function parameter scope.

C другой стороны, поиск имён для инициализаторов в списке инициализации конструктора начинается в области видимости параметров конструктора.

class.base.init / 15:

The expression-list or braced-init-list of a mem-initializer is in the function parameter scope of the constructor and can use this to refer to the object being initialized.

class X {
  int a;
  int b;
  int i;
  int j;
public:
  const int& r;
  X(int i): r(a), b(i), i(i), j(this->i) { }
};

initializes X​::​r to refer to X​::​a, initializes X​::​b with the value of the constructor parameter i, initializes X​::​i with the value of the constructor parameter i, and initializes X​::​j with the value of X​::​i; this takes place each time an object of class X is created.

Поэтому здесь

struct S {
    int m = 41;
    S(int m = 42): m{m} { std::cout << m << '\n'; }
};

член m структуры S инициализируется параметром m конструктора S.


Поведение конструкции

int m = 41;
{int m = m;}

описывается другим разделом стандарта языка.

basic.scope.pdecl, Point of declaration:

1 The locus of a declaration ([basic.pre]) that is a declarator is immediately after the complete declarator ([dcl.decl]).
[Example 1:
unsigned char x = 12;
{ unsigned char x = x; }
Here, the initialization of the second x has undefined behavior, because the initializer accesses the second x outside its lifetime ([basic.life]). — end example]
2 [Note 1: A name from an outer scope remains visible up to the locus of the declaration that hides it.
[Example 2:
const int i = 2;
{ int i[i]; }
declares a block-scope array of two integers. — end example]
— end note]

После того как переменная объявлена {int m ...}, она скрывает объявление во внешней области видимости int m = 41;.

Так как время жизни объекта начинается после его инициализации, то здесь

{int m = m;}

мы пытаемся прочитать значение объекта, который ещё не существует. Поведение программы не определено.

→ Ссылка