cerr << __func__ << ": array size is " << size << endl;
#endif
// ...
Здесь переменная __func__
используется для вывода имени отлаживаемой функции. Компилятор определяет переменную __func__
в каждой функции. Это локальный статический массив типа const char
, содержащий имя функции.
Кроме переменной __func__,
определяемой компилятором С++, препроцессор определяет четыре других имени, которые также могут пригодиться при отладке:
__FILE__
строковый литерал, содержащий имя файла.
__LINE__
целочисленный литерал, содержащий номер текущий строки.
__TIME__
строковый литерал, содержащий файл и время компиляции.
__DATE__
строковый литерал, содержащий файл и дату компиляции.
Эти константы можно использовать для отображения дополнительной информации в сообщениях об ошибках:
if (word.size() < threshold)
cerr << "Error: " << __FILE__
<< " : in function " << __func__
<< " at line " << __LINE__ << endl
<< " Compiled on " << __DATE__
<< " at " << __TIME__ << endl
<< " Word read was \"" << word << "\": Length too short" << endl;
Если передать этой программе строку, которая короче threshold
, то будет создано следующее сообщение об ошибке:
Error: wdebug.cc : in function main at line 27
Compiled on Jul 11 2012 at 20:50:03
Word read was "foo": Length too short
Упражнение 6.47. Пересмотрите программу, написанную в упражнении раздела 6.3.2, где использовалась рекурсия для отображения содержимого вектора так, чтобы условно отображать информацию о ее выполнении. Например, отобразите размер вектора при каждом вызове. Откомпилируйте и запустите программу с включенной отладкой и с выключенной.
Упражнение 6.48. Объясните, что делает этот цикл и стоит ли использовать в нем макрос assert
:
string s;
while (cin >> s && s != sought) { } // пустое тело
assert(cin);
Во многих (если не во всех) случаях довольно просто выяснить, какая из перегруженных версий функции будет использована при данном вызове. Но это не так просто, когда у перегруженных функций одинаковое количество параметров и когда один или несколько параметров имеют типы, связанные преобразованиями. Для примера рассмотрим следующий набор перегруженных функций и их вызов:
void f() ;
void f(int) ;
void f(int, int);
void f(double, double = 3.14);
f(5.6); // вызов void f(double, double)
На первом этапе подбора перегруженной функции выявляют набор версий, подходящих для рассматриваемого вызова. Такие функции называются f
.
На втором этапе выбора функции из набора кандидатов выявляются те, которые могут быть вызваны с аргументами данного вызова. Выбранные функции называют
При вызове f(5.6)
две функции-кандидата можно исключить сразу из-за несоответствия количеству аргументов. Речь идет о версии без параметров и версии с двумя параметрами типа int
. В данном случае вызов имеет только один аргумент, а эти функции не имеют их вообще или имеют два параметра соответственно.
Функция, получающая один аргумент типа int
, и функция, получающая два аргумента типа double
, могли бы быть подходящими. Любая из них может быть вызвана с одним аргументом. Функция, получающая два аргумента типа double
, имеет аргумент по умолчанию, а значит, может быть вызвана с одним аргументом.
После проверки количества аргументов, позволяющей выявить функции, подходящие потенциально, проверяется соответствие типов параметров функций типам аргументов, переданных при вызове. Как и при любом обращении, тип аргумента может либо совпадать, либо допускать преобразование в тип параметра. В данном случае подходят обе оставшиеся функции.