9.2. Три шага разрешения перегрузки
9.2. Три шага разрешения перегрузки
Разрешением перегрузки функции называется процесс выбора той функции из множества перегруженных, которую следует вызвать. Этот процесс основывается на указанных при вызове аргументах. Рассмотрим пример:
T t1, t2;
void f( int, int );
void f( float, float );
int main() {
f( t1, t2 );
return 0;
}
Здесь в ходе процесса разрешения перегрузки в зависимости от типа T определяется, будет ли при обработке выражения f(t1,t2) вызвана функция f(int,int) или f(float,float) или зафиксируется ошибка.
Разрешение перегрузки функции – один и самых сложных аспектов языка C++. Пытаясь разобраться во всех деталях, начинающие программисты столкнутся с серьезными трудностями. Поэтому в данном разделе мы представим лишь краткий обзор того, как происходит разрешение перегрузки, чтобы у вас составилось хоть какое-то впечатление об этом процессе. Для тех, кто хочет узнать больше, в следующих двух разделах приводится более подробное описание.
Процесс разрешения перегрузки функции состоит из трех шагов, которые мы покажем на следующем примере:
void f();
void f( int );
void f( double, double = 3.4 );
void f( char *, char * );
void main() {
f( 5.6 );
return 0;
}
При разрешении перегрузки функции выполняются следующие шаги:
* Выделяется множество перегруженных функций для данного вызова, а также свойства списка аргументов, переданных функции.
* Выбираются те из перегруженных функций, которые могут быть вызваны с данными аргументами, с учетом их количества и типов.
* Находится функция, которая лучше всего соответствует вызову.
Рассмотрим последовательно каждый пункт.
На первом шаге необходимо идентифицировать множество перегруженных функций, которые будут рассматриваться при данном вызове. Вошедшие в это множество функции называются кандидатами. Функция-кандидат – это функция с тем же именем, что и вызванная, причем ее объявление видимо в точке вызова. В нашем примере есть четыре таких кандидата: f(), f(int), f(double, double) и f(char*, char*).
После этого идентифицируются свойства списка переданных аргументов, т.е. их количество и типы. В нашем примере список состоит из двух аргументов типа double.
На втором шаге среди множества кандидатов отбираются устоявшие (viable) – такие, которые могут быть вызваны с данными аргументами, Устоявшая функция либо имеет столько же формальных параметров, сколько фактических аргументов передано вызванной функции, либо больше, но тогда для каждого дополнительного параметра должно быть задано значение по умолчанию. Чтобы функция считалась устоявшей, для любого фактического аргумента, переданного при вызове, обязано существовать преобразование к типу формального параметра, указанного в объявлении.
В нашем примере есть две устоявших функции, которые могут быть вызваны с приведенными аргументами:
* функция f(int) устояла, потому что у нее есть всего один параметр и существует преобразование фактического аргумента типа double к формальному параметру типа int;
* функция f(double,double) устояла, потому что для второго аргумента есть значение по умолчанию, а первый формальный параметр имеет тип double, что в точности соответствует типу фактического аргумента.
Если после второго шага не нашлось устоявших функций, то вызов считается ошибочным. В таких случаях мы говорим, что имеет место отсутствие соответствия.
Третий шаг заключается в выборе функции, лучше всего отвечающей контексту вызова. Такая функция называется наилучшей из устоявших (или наиболее подходящей). На этом шаге производится ранжирование преобразований, использованных для приведения типов фактических аргументов к типам формальных параметров устоявшей функции. Наиболее подходящей считается функция, для которой выполняются следующие условия:
преобразования, примененные к фактическим аргументам, не хуже преобразований, необходимых для вызова любой другой устоявшей функции;
для некоторых аргументов примененные преобразования лучше, чем преобразования, необходимые для приведения тех же аргументов в вызове других устоявших функций.
Преобразования типов и их ранжирование более подробно обсуждаются в разделе 9.3. Здесь мы лишь кратко рассмотрим ранжирование преобразований для нашего примера. Для устоявшей функции f(int) должно быть применено приведение фактического аргумента типа double к типу int, относящееся к числу стандартных. Для устоявшей функции f(double,double) тип фактического аргумента double в точности соответствует типу формального параметра. Поскольку точное соответствие лучше стандартного преобразования (отсутствие преобразования всегда лучше, чем его наличие), то наиболее подходящей функцией для данного вызова считается f(double,double).
Если на третьем шаге не удается отыскать единственную лучшую из устоявших функцию, иными словами, нет такой устоявшей функции, которая подходила бы больше всех остальных, то вызов считается неоднозначным, т.е. ошибочным.
(Более подробно все шаги разрешения перегрузки функции обсуждаются в разделе 9.4. Процесс разрешения используется также при вызовах перегруженной функции-члена класса и перегруженного оператора. В разделе 15.10 рассматриваются правила разрешения перегрузки, применяемые к функциям-членам класса, а в разделе 15.11 – правила для перегруженных операторов. При разрешении перегрузки следует также принимать во внимание функции, конкретизированные из шаблонов. В разделе 10.8 обсуждается, как шаблоны влияют на такое разрешение.)
Упражнение 9.5
Что происходит на последнем (третьем) шаге процесса разрешения перегрузки функции?
Более 800 000 книг и аудиокниг! 📚
Получи 2 месяца Литрес Подписки в подарок и наслаждайся неограниченным чтением
ПОЛУЧИТЬ ПОДАРОКЧитайте также
Образцы шага, часть 1: оси образца
Образцы шага, часть 1: оси образца Оси — первая часть образцов шага. Например, в образце шага child::NAME, ссылающемся на элемент <NAME>, дочерний по отношению к контекстному узлу, child называется осью. У образцов две оси:• ось attribute содержит атрибуты контекстного узла;• ось child
Образцы шага, часть 2: условия узла
Образцы шага, часть 2: условия узла Условия узла (node test) составляют вторую часть образцов шага. В качестве условий узла можно использовать названия узлов или символ подстановки * для выбора и узлов, и их типов. Например, выражение child::*/child::NAME выбирает все элементы <NAME>,
Три шага к свободному ПО
Три шага к свободному ПО Не все знают, что стоимость проприетарного (коммерческого) программного обеспечения, способного превратить «компьютерное железо» в современный компьютер, значительно больше стоимости самого «железа». Не знают зачастую потому, что используют
Определение шага привязки
Определение шага привязки В режиме шаговой привязки SNAP курсор может находиться только в определенных точках согласно установленному значению шага и при этом движется не плавно, а скачкообразно между узлами воображаемой сетки, как бы «прилипая» к ее узлам. Активность
3.5. Разрешения IPC
3.5. Разрешения IPC При создании нового объекта IPC с помощью одной из функций getXXX, вызванной с флагом IPC_CREAT, в структуру ipc_perm заносится следующая информация (раздел 3.3):1. Часть битов аргумента oflag задают значение поля mode структуры ipc_perm. В табл. 3.3 приведены биты разрешений для
Задание шага цикла
Задание шага цикла Полный синтаксис оператора For. . .Next включает необязательное ключевое слово Step (шаг) в первой строке структуры, как, например, в следующем фрагменте программного кода: Sub ListOddNumbers() Dim strOddNumbers As String For F = 1 To 33 Step 2 StrOddNumbers = strOddNumbers 5 F & " " Next F MsgBox "Нечетными
30. Избегайте перегрузки && , || и , (запятой)
30. Избегайте перегрузки &&, || и , (запятой) РезюмеМудрость — это знание того, когда надо воздержаться. Встроенные операторы &&, || и , (запятая) трактуются компилятором специальным образом. После перегрузки они становятся обычными функциями с весьма отличной
Определение шага привязки
Определение шага привязки В режиме шаговой привязки SNAP курсор может находиться только в определенных точках согласно установленному значению шага и при этом движется не плавно, а скачкообразно между узлами воображаемой сетки, как бы «прилипая» к ним. Активность режима
15.10.1. Еще раз о разрешении перегрузки функций
15.10.1. Еще раз о разрешении перегрузки функций В главе 9 подробно описывалось, как разрешается вызов перегруженной функции. Если фактические аргументы при вызове имеют тип класса, указателя на тип класса или указателя на члены класса, то на роль возможных кандидатов
15.11. Разрешение перегрузки и функции-члены A
15.11. Разрешение перегрузки и функции-члены A * Функции-члены также могут быть перегружены, и в этом случае тоже применяется процедура разрешения перегрузки для выбора наилучшей из устоявших. Такое разрешение очень похоже на аналогичную процедуру для обычных функций и
15.12. Разрешение перегрузки и операторы A
15.12. Разрешение перегрузки и операторы A В классах могут быть объявлены перегруженные операторы и конвертеры. Предположим, при инициализации встретился оператор сложения:SomeClass sc;int iobj = sc + 3;Как компилятор решает, что следует сделать: вызвать перегруженный оператор для
19.3. Разрешение перегрузки и наследование A
19.3. Разрешение перегрузки и наследование A * Наследование классов оказывает влияние на все аспекты разрешения перегрузки функций (см. раздел 9.2). Напомним, что эта процедура состоит из трех шагов: Отбор функций-кандидатов.* Отбор устоявших функций.* Выбор наилучшей из
Определение шага привязки
Определение шага привязки В режиме шаговой привязки SNAP курсор может находиться только в определенных точках согласно установленному значению шага и при этом движется не плавно, а скачкообразно между узлами воображаемой сетки, как бы «прилипая» к ее узлам. Активность
Определение шага привязки
Определение шага привязки В режиме шаговой привязки SNAP курсор может находиться только в определенных точках согласно установленному значению шага и при этом движется не плавно, а скачкообразно между узлами воображаемой сетки, как бы «прилипая» к ее узлам.Шаговая