Как работают потоковые манипуляторы с аргументами? - PullRequest
8 голосов
/ 29 октября 2011

В книге Страуструпа по С ++ приведен пример пользовательского манипулятора, принимающего аргумент (см. Прилагаемый код). Я запутался в том, как создается структура. В частности, похоже, что есть два аргумента int для конструктора «smanip», один для указателя функции «ff», другой для «ii». Я не понимаю, как аргумент int передается для создания структуры с помощью:

cout << setprecision(4) << angle;

Кроме того, каков порядок вызова этих функций и как определяются аргументы типа Ch и Tr? Большое спасибо.

// manipulator taking arguments
struct smanip{
    iso_base& (*f) (ios_base&, int);
    int i;
    smanip(ios_base& (*ff)(ios_base&, int), int ii) : f(ff), i(ii){}
};

template<cladd Ch, class Tr>
ostream<Ch, Tr>& operator<<(ostream<Ch, Tr>& os, smanip& m){
    return m.f(os, m.i);
}

ios_base& set_precision(ios_base& 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;

Ответы [ 2 ]

9 голосов
/ 29 октября 2011
setprecision(4)

звонки

inline smanip setprecision(int n){
    return smanip(set_precision,n);
}

, который создает smanip из указателя на функцию set_precision и n.

struct smanip{
    ios_base& (*f) (ios_base&, int);
    int i;
    smanip(ios_base& (*ff)(ios_base&, int), int ii) : f(ff), i(ii){}
};

smanip - это структура, которая содержит указатель на функцию и целое число. Эта функция принимает ios_base по ссылке и int и возвращает ios_base по ссылке.

На этом этапе линия выглядит примерно так:

smanip m(&setprecision, 4);
cout << m << (otherstuff);

что соответствует этому шаблону:

template<class Ch, class Tr>
ostream<Ch, Tr>& operator<<(ostream<Ch, Tr>& os, smanip& m){
    return m.f(os, m.i);
}

И компилятор может вывести Ch и Tr из потока на левой стороне. В этом случае std::cout. Код выполняет m.f(os, m.i). Это вызывает указатель функции, удерживаемый smanip, передавая ему поток, а целое число удерживается smanip.

ios_base& set_precision(ios_base& s, int n){
    return s.setprecision(n); // call the member function
}

Звонит cout.setprecision(n).

Таким образом, строка переводится в:

std::cout.setprecision(4) << angle;
0 голосов
/ 29 октября 2011

Функтор манипулятора принимает указатель функции и int в качестве аргументов и сохраняет их внутри для последующего использования. Для удобства чтения сигнатура конструктора может быть разделена на эти две декларации:

typedef ios_base& (*f_ptr)(ios_base&,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 == &set_precision
                          //                                m.i == 4 )
...