How do stream manipulators with arguments work?

蓝咒 提交于 2020-01-01 04:57:10

问题


In Stroustrup's C++ book, there is an example of a custom manipulator taking an argument (pls see the attached code). I am confused about how the struct is created. In particular, it looks like there are two int arguments for the constructor of "smanip", one for the function pointer "ff", one for "ii". I don't understand how the int argument is passed to create the structure by using:

cout << setprecision(4) << angle;

In addition, what is the order these functions get called, and how the the type arguments Ch and Tr are determined? Thanks a lot.

// 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;

回答1:


setprecision(4)

calls

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

Which creates an smanip from a pointer to the set_precision function, and 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 is a struct that holds a pointer to a function, and an integer. That function takes an ios_base by reference and an int, and returns the ios_base by reference.

At this point the line is effectively like this:

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

which matches this template:

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

And the compiler can deduce Ch, and Tr from the stream on the left side. In this case, std::cout. The code executes m.f(os, m.i). This calls the function pointer held by the smanip, passing it the stream and the integer held by smanip.

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

This calls cout.setprecision(n).

So the line translates to:

std::cout.setprecision(4) << angle;



回答2:


The manipulator functor takes a function pointer and an int as arguments, and it stores both internally for later use. The signature of the constructor can be split in this two declarations for readability:

typedef ios_base& (*f_ptr)(ios_base&,int);
smanip( f_ptr f, int )

That is, the first argument is the function pointer and the second is the value.

As of the order of execution in the example code, first the function setprecision is called, that function stores the function pointer and value inside the smanip object and returns it. The object is passed to the appropriate operator<<, that extracts and executes the stored function pointer on the current stream passing the argument.

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


来源:https://stackoverflow.com/questions/7935181/how-do-stream-manipulators-with-arguments-work

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!