Это называется с использованием объявления . Есть два способа использования ключевого слова using
. Существует третья специальная форма использования объявлений, используемых внутри определений классов, , но я остановлюсь здесь на общем использовании объявлений. (см. Ниже).
- с использованием декларации
- с использованием директивы
У них два совершенно разных эффекта. Использование объявление объявляет имя как псевдоним другого объявления или набора объявлений (если вы должны были назвать набор перегруженных функций). Имя объявлено в текущей области действия . То есть вы тоже можете использовать его внутри блоков
int main() {
using std::swap;
// ...
}
Это очень полезно, если вы используете имя очень часто локально, и вы не хотите использовать префикс для него во всех случаях, а также полезно при реализации подкачки с использованием зависимой от аргумента идиомы поиска.
A с использованием директивы присваивает имена пространству имен и не объявляет никаких имен. Вместо этого он изменит поиск имен, чтобы найти имена, которые на самом деле не объявлены там, где, по его мнению, они находятся. Для неквалифицированного поиска имени он находит имена, объявленные во включающем пространстве имен, которое охватывает как директиву using, так и целевое пространство имен. Будут найдены все имена, которые объявлены в целевых пространствах имен:
int cout;
int main() {
using namespace std;
// cout << 1; ambiguous!
}
Здесь cout
будет считаться дважды объявленным в глобальном пространстве имен и вызывает неоднозначность (::
включает в себя как main
, так и std
). В квалифицированном namelookup он будет создавать транзитивное закрытие пространства имен со всеми пространствами имен, указанными в директивах using.
using namespace foo;
int main() {
::c++;
}
c
не только ищется в глобальном пространстве имен, но также и в пространстве имен foo
и в пространствах имен, для которых foo
использует директивы для и так далее. Однако если глобальное пространство имен будет содержать прямое объявление (включая объявление использования), то это объявление будет скрывать декларации, найденные косвенно с помощью директив:
using namespace foo;
int c;
int main() {
::c++; // not ambiguous!
}
Использование объявлений может появляться во многих местах, в том числе внутри определений классов. Его значение аналогично его значению в другом месте с важным ограничением: оно объявляет имя как псевдоним одного или нескольких объявлений, но объявления должны быть членами базового класса. Это очень полезно для отображения имен в производном классе, которые в противном случае были бы скрыты под тем же именем, объявленным там
struct base {
void f();
};
struct derived : base {
using base::f; // name "f" declared in derived
void f(int); // overloads the using declaration
};
Теперь вы можете позвонить d.f()
. Если бы не было никакого объявления об использовании, тогда поиск имени нашел бы только одно объявление f
в derived
и остановил бы поиск, не углубляясь в область действия базового класса:
derived d;
d.f(); // invalid without the using declaration
d.f(0); // valid with or without the using declaration
// explicitly starting lookup in base: valid with or without the using declaration
d.base::f();
Это также позволяет изменить доступность членов базового класса, хотя вы должны использовать это экономно:)
На практике мне показалось полезным сделать виртуальную функцию-член повторно видимой:
struct base {
virtual void f();
virtual void f(int);
};
struct derived : base {
// using base::f; would solve it
virtual void f() { ... }
};
Упс - теперь d.f(0);
недопустим, потому что поиск имени находит только нулевой параметр f
! Директива using решит эту проблему. Обратите внимание, что если вы создаете псевдоним объявления функции, который имеет те же типы параметров и константу, что и явное объявление (например, f()
в этом случае), то явное объявление все равно будет скрывать то, для которого объявление using является псевдонимом - так что оба f()
функции не будут конфликтовать в этом случае.
Альтернативой для решения этой проблемы является использование идиома не виртуального интерфейса
struct base {
void f() { do_f(); }
void f(int) { do_f(0); }
private:
virtual void do_f();
virtual void do_f(int);
};
struct derived : base {
private:
virtual void do_f() { ... }
};
struct derived1 : derived {
private:
virtual void do_f(int) { ... }
};
Теперь и d.f(0)
, и d.f()
действительны независимо от того, какой объект вы называете.