Как работают потоковые манипуляторы с аргументами?

#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 )