В C ++, когда две переменные с одинаковым именем могут быть видны в одной и той же области видимости? - PullRequest
6 голосов
/ 06 июня 2009

Этот код иллюстрирует то, что, по моему мнению, следует рассматривать как плохую практику, и вызывает предупреждения от компилятора о переопределении или маскировании переменной:

#include <iostream>

int *a;

int* f()
{
  int *a = new int;
  return a;
}

int main()
{
  std::cout << a << std::endl << f() << std::endl;
  return 0;
}

Вывод (скомпилирован с g ++):

0
0x602010

Я просмотрел несколько ссылок (Stroustrup и The Complete C ++ Reference) и не могу найти ничего, когда и почему это разрешено. Я знаю, что это не входит ни в одну локальную область.

Когда и почему это разрешено? Есть ли хорошее применение для этой конструкции? Как я могу получить g ++, чтобы предупредить меня об этом? Другие компиляторы кричат ​​об этом?

Ответы [ 6 ]

15 голосов
/ 06 июня 2009

Относительно того, почему это разрешено: это совершенно верно.

Когда вы находитесь в своей функции f (), вы определяете локальную область видимости. Локальные области действия переопределяют глобальную область, поэтому определение вашей переменной "a" там "скрывает" глобальную int *a;

9 голосов
/ 06 июня 2009

Это совершенно верно, но я думаю, что с -Wall вы получаете предупреждение только тогда, когда вы скрываете параметр.

Если вы хотите получать предупреждения, когда вы скрываете переменную любого типа , вы можете использовать ее со страницы руководства g++:

   -Wshadow
       Warn whenever a local variable shadows another local variable, 
       parameter or global variable or whenever a built-in function is 
       shadowed.

Обратите внимание, что -Wshadow по умолчанию не включено в -Wall.

6 голосов
/ 06 июня 2009

Это разрешено, так что вы можете спокойно игнорировать переопределение глобального идентификатора. По сути, вам нужно иметь дело только с глобальными именами, которые вы фактически используете.

Предположим, в вашем примере, f() был определен первым. Затем какой-то другой разработчик добавил глобальную декларацию. Добавив имя, f(), которое раньше работало, все еще работает. Если переопределение было ошибкой, то функция внезапно перестала бы работать, даже если с добавленной глобальной переменной она вообще ничего не делает.

4 голосов
/ 06 июня 2009

Многие языки допускают подобные вещи.
Обычно (по отношению ко всем языкам) самой локальной переменной является та, на которую вы тоже ссылаетесь. Из более чем 20 языков, которые я использовал, это очень распространено.

Также большинство языков позволяют вам явно ссылаться на тот, что находится во внешней области.
Например, C ++ позволяет указывать переменную в глобальной области видимости с помощью оператора ::.

#include  <iostream>


int a = 5;
int main()
{
    int a = 6;

    std::cout << a << "\n" << ::a << "\n";
            // Local
                           // global
}
1 голос
/ 08 июня 2009

Ответить, когда это разрешено: в основном в любых двух вложенных областях.

Например:

void foo() {
    int a;
    {
        int a;
    }
}

class Base {
    int a;
};
class Derived: public Base {
    int a; // Yes, the name Base::a is visible in the scope of Derived, even if private
};

class Foo() {
    int a;
    Foo(int a) : a(a) { } // Works OK
};

using std::swap;
void swap(MyClass& lhs, MyClass& rhs);
// Not strictly a variable, but name lookup in C++ happens before determining 
// what the name means.

Теперь ответ должен быть однозначным: обычно допускается наличие двух «вещей» с одним именем в одной и той же области. Это возможно, потому что самое большее одно из имен фактически определено в этой области; другие были бы просто видимыми в этой области. Правила разрешения имен определяют, какое имя выбрано, если есть несколько кандидатов.

Вы действительно не хотите давать предупреждение для каждого случая, когда компилятор выбирает между альтернативами. Это даст вам массу предупреждений о таких невинных вещах, как перегрузка и некоторый умный шаблонный код.

0 голосов
/ 06 июня 2009

Как уже упоминалось, это совершенно законно и недвусмысленно для компилятора.

Тем не менее, это одна из многих функций в языках программирования, которая может вызвать путаницу или трудно найти ошибки. Поскольку для простоты было бы легко дать разные имена каждой из этих переменных, я бы всегда предлагал это сделать.

...