Перегрузка и область видимости A
Все перегруженные функции объявляются в одной и той же области видимости. К примеру, локально объявленная функция не перегружает, а просто скрывает глобальную:
#include <string> void print( const string & ); void print( double ); // перегружает print() void fooBar( int ival ) { // отдельная область видимости: скрывает обе реализации print() extern void print( int ); // ошибка: print( const string & ) не видна в этой области print( "Value: "); print( ival ); // правильно: print( int ) видна |
}
Поскольку каждый класс определяет собственную область видимости, функции, являющиеся членами двух разных классов, не перегружают друг друга. (Функции-члены класса описываются в главе 13. Разрешение перегрузки для функций-членов класса рассматривается в главе 15.)
Объявлять такие функции разрешается и внутри пространства имен. С каждым из них также связана отдельная область видимости, так что функции, объявленные в разных пространствах, не перегружают друг друга. Например:
#include <string> namespace IBM { extern void print( const string & ); extern void print( double ); // перегружает print() } namespace Disney { // отдельная область видимости: // не перегружает функцию print() из пространства имен IBM extern void print( int ); |
}
Использование using-объявлений и using-директив помогает сделать члены пространства имен доступными в других областях видимости. Эти механизмы оказывают определенное влияние на объявления перегруженных функций. (Using-объявления и using-директивы рассматривались в разделе 8.6.)
Каким образом using-объявление сказывается на перегрузке функций? Напомним, что оно вводит псевдоним для члена пространства имен в ту область видимости, в которой это объявление встречается. Что делают такие объявления в следующей программе?
namespace libs_R_us { int max( int, int ); int max( double, double ); extern void print( int ); extern void print( double ); } // using-объявления using libs_R_us::max; using libs_R_us::print( double ); // ошибка void func() { max( 87, 65 ); // вызывает libs_R_us::max( int, int ) |
max( 35.5, 76.6 ); // вызывает libs_R_us::max( double, double )
Первое using-объявление вводит обе функции libs_R_us::max в глобальную область видимости. Теперь любую из функций max() можно вызвать внутри func(). По типам аргументов определяется, какую именно функцию вызывать. Второе using-объявление – это ошибка: в нем нельзя задавать список параметров. Функция libs_R_us::print()
объявляется только так:
using libs_R_us::print;
Using-объявление всегда делает доступными все
перегруженные функции с указанным именем. Такое ограничение гарантирует, что интерфейс пространства имен libs_R_us не будет нарушен. Ясно, что в случае вызова
print( 88 );
автор пространства имен ожидает, что будет вызвана функция libs_R_us::print(int). Если разрешить пользователю избирательно включать в область видимости лишь одну из нескольких перегруженных функций, то поведение программы становится непредсказуемым.
Что происходит, если using-объявление вводит в область видимости функцию с уже существующим именем? Эти функции выглядят так, как будто они объявлены прямо в том месте, где встречается using-объявление. Поэтому введенные функции участвуют в процессе разрешения имен всех перегруженных функций, присутствующих в данной области видимости:
#include <string> namespace libs_R_us { extern void print( int ); extern void print( double ); } extern void print( const string & ); // libs_R_us::print( int ) и libs_R_us::print( double ) // перегружают print( const string & ) using libs_R_us::print; void fooBar( int ival ) { print( "Value: "); // вызывает глобальную функцию // print( const string & ) print( ival ); // вызывает libs_R_us::print( int ) |
Using-объявление добавляет в глобальную область видимости два объявления: для print(int) и для print(double). Они являются псевдонимами в пространстве libs_R_us и включаются в множество перегруженных функций с именем print, где уже находится глобальная print(const string &). При разрешении перегрузки print в fooBar
рассматриваются все три функции.
Если using-объявление вводит некоторую функцию в область видимости, в которой уже имеется функция с таким же именем и таким же списком параметров, это считается ошибкой. С помощью using-объявления нельзя задать псевдоним для функции print(int) в пространстве имен libs_R_us, если в глобальной области видимости уже есть print(int). Например:
namespace libs_R_us { void print( int ); void print( double ); } void print( int ); using libs_R_us::print; // ошибка: повторное объявление print(int) void fooBar( int ival ) { print( ival ); // какая print? ::print или libs_R_us::print |
Мы показали, как связаны using-объявления и перегруженные функции. Теперь рассмотрим особенности применения using-директивы. Using-директива приводит к тому, что члены пространства имен выглядят объявленными вне этого пространства, добавляя их в новую область видимости. Если в этой области уже есть функция с тем же именем, то происходит перегрузка. Например:
#include <string> namespace libs_R_us { extern void print( int ); extern void print( double ); } extern void print( const string & ); // using-директива // print(int), print(double) и print(const string &) - элементы // одного и того же множества перегруженных функций using namespace libs_R_us; void fooBar( int ival ) { print( "Value: "); // вызывает глобальную функцию // print( const string & ) print( ival ); // вызывает libs_R_us::print( int ) |
Это верно и в том случае, когда есть несколько using-директив. Одноименные функции, являющиеся членами разных пространств, включаются в одно и то множество:
namespace IBM { int print( int ); } namespace Disney { double print( double ); |
// using-директива // формируется множество перегруженных функций из различных // пространств имен using namespace IBM; using namespace Disney; long double print(long double); int main() { print(1); // вызывается IBM::print(int) print(3.1); // вызывается Disney::print(double) return 0; |
Множество перегруженных функций с именем print в глобальной области видимости включает функции print(int), print(double) и print(long double). Все они рассматриваются в main() при разрешении перегрузки, хотя первоначально были определены в разных пространствах имен.
Итак, повторим, что перегруженные функции находятся в одной и той же области видимости. В частности, они оказываются там в результате применения using-объявлений и using-директив, делающих доступными имена из других областей.