Когда в C++ две переменные с одинаковым именем могут быть видны в одной области?
-
12-09-2019 - |
Вопрос
Этот код иллюстрирует то, что, по моему мнению, следует рассматривать как плохую практику и вызывать предупреждения компилятора о переопределении или маскировке переменной:
#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++ предупредить меня об этом?Другие компиляторы кричат об этом?
Решение
Это разрешено, чтобы вы могли безопасно игнорировать переопределение глобального идентификатора.По сути, вас должны интересовать только глобальные имена, которые вы действительно используете.
Предположим, в вашем примере f()
были определены в первую очередь.Затем какой-то другой разработчик добавил глобальную декларацию.Добавив имя, f()
что раньше работало, работает до сих пор.Если бы переопределение было ошибкой, функция внезапно перестала бы работать, хотя она вообще ничего не делает с вновь добавленной глобальной переменной.
Другие советы
Почему это разрешено:это совершенно справедливо.
Когда вы находитесь внутри функции f(), вы определяете локальную область видимости.Локальные области переопределяют глобальную область, поэтому определение там переменной «a» «скрывает» глобальную область. int *a;
Это совершенно верно, но я думаю, что с -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
по умолчанию.
Многие языки допускают подобные вещи.
Обычно (по отношению ко всем языкам) наиболее локально определяемая переменная — это та, на которую вы ссылаетесь.Из более чем 20 языков, которые я использовал, этот очень распространен.
Кроме того, большинство языков позволяют вам явно ссылаться на тот, который находится во внешней области.
Например, C++ позволяет указать переменную в глобальной области видимости с помощью ::оператор.
#include <iostream>
int a = 5;
int main()
{
int a = 6;
std::cout << a << "\n" << ::a << "\n";
// Local
// global
}
Чтобы ответить, когда это разрешено:в основном в любых двух вложенных областях.
Например:
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.
Теперь ответ должен быть очевидным: наличие двух «вещей» с одним именем в одной области действия обычно разрешено.Это возможно, потому что не более одного из имен на самом деле определенный в этом объеме;другие были бы просто видимый в этом объеме.Правила разрешения имен определяют, какое имя выбирается, если имеется несколько кандидатов.
На самом деле вы не хотите выдавать предупреждение в каждом случае, когда компилятор выбирает между альтернативами.Это даст вам массу предупреждений о таких невинных вещах, как перегрузка и некоторый умный код шаблона.
Как уже отмечали другие, это совершенно законно и однозначно для компилятора.
Однако это одна из многих особенностей языков программирования, которая может вызвать путаницу или труднообнаружимые ошибки.Поскольку было бы тривиально дать разные имена каждой из этих переменных, для ясности я всегда предлагаю делать это.