#c #stream #manipulators
#c #поток #манипуляторы
Вопрос:
В книге Страуструпа по C есть пример пользовательского манипулятора, принимающего аргумент (см. прилагаемый код). Я не понимаю, как создается структура. В частности, похоже, что для конструктора «smanip» есть два аргумента int, один для указателя на функцию «ff», один для «ii». Я не понимаю, как передается аргумент int для создания структуры с помощью:
cout << setprecision(4) << angle;
Кроме того, в каком порядке вызываются эти функции и как определяются аргументы типа Ch и Tr? Большое спасибо.
// manipulator taking arguments
struct smanip{
iso_baseamp; (*f) (ios_baseamp;, int);
int i;
smanip(ios_baseamp; (*ff)(ios_baseamp;, int), int ii) : f(ff), i(ii){}
};
template<cladd Ch, class Tr>
ostream<Ch, Tr>amp; operator<<(ostream<Ch, Tr>amp; os, smanipamp; m){
return m.f(os, m.i);
}
ios_baseamp; set_precision(ios_baseamp; s, int n){
return s.setprecision(n); // call the member function
}
inline smanip setprecision(int n){
return smanip(set_precision,n);
}
// usage:
cout << setprecision(4) << angle;
Комментарии:
1. В этом коде на самом деле нет никакого «функционального объекта». Задействованы три функции с очень похожими именами:
set_precision
,setprecision
, иios_base::setprecision
. Если вы не уделяете пристального внимания, их легко запутать.
Ответ №1:
setprecision(4)
вызовы
inline smanip setprecision(int n){
return smanip(set_precision,n);
}
Который создает smanip
из указателя на set_precision
функцию, и n
.
struct smanip{
ios_baseamp; (*f) (ios_baseamp;, int);
int i;
smanip(ios_baseamp; (*ff)(ios_baseamp;, int), int ii) : f(ff), i(ii){}
};
smanip
это структура, которая содержит указатель на функцию и целое число. Эта функция принимает an ios_base
по ссылке и an int
и возвращает ios_base
по ссылке.
На данный момент строка выглядит примерно так:
smanip m(amp;setprecision, 4);
cout << m << (otherstuff);
который соответствует этому шаблону:
template<class Ch, class Tr>
ostream<Ch, Tr>amp; operator<<(ostream<Ch, Tr>amp; os, smanipamp; m){
return m.f(os, m.i);
}
И компилятор может выводить Ch
, и Tr
из потока с левой стороны. В этом случае std::cout
. Код выполняется m.f(os, m.i)
. Это вызывает указатель на функцию, удерживаемый smanip
, передавая ему поток и целое число, удерживаемое smanip
.
ios_baseamp; set_precision(ios_baseamp; s, int n){
return s.setprecision(n); // call the member function
}
Это вызывает cout.setprecision(n)
.
Итак, строка преобразуется в:
std::cout.setprecision(4) << angle;
Ответ №2:
Функтор манипулятора принимает указатель на функцию и значение int в качестве аргументов и сохраняет их внутри для последующего использования. Подпись конструктора может быть разделена в этих двух объявлениях для удобства чтения:
typedef ios_baseamp; (*f_ptr)(ios_baseamp;,int);
smanip( f_ptr f, int )
То есть первый аргумент — это указатель на функцию, а второй — значение.
Что касается порядка выполнения в примере кода, сначала setprecision
вызывается функция, эта функция сохраняет указатель на функцию и значение внутри smanip
объекта и возвращает его. Объект передается соответствующему operator<<
, который извлекает и выполняет сохраненный указатель на функцию в текущем потоке, передающем аргумент.
// on the calling end
setprecision(4) // --> construct __s = smanip( set_precision, 4 )
(cout << ) // --> passes __s to `operator<<`
// inside operator<<
return m.f( os, m.i ); // calls: set_precision( os, 4 ) (m.f == amp;set_precision
// m.i == 4 )