Sign in to follow this  
adder_noir

STL Constructed Function Object :oO

Recommended Posts

Hi!

I think I'm going to have to buy some of my regular responders a Christmas present for all the help I've received lately! Alas tho I have yet another question, and there are many many more to come. The road is long, with many a winding turn as they say ;oD

ANyway check out this code:

struct Popless : public std::binary_function<State, State, bool> {
bool operator () (const State &a, const State &b) const
{return popLess (a, b);} // popLess is an inline function
};



Ok now normally this would seriously FREAK me out. I also know Zahlman hates me calling it the STL but I don't know how else to refer to it ;o) It isn't quite as scary as it should be tho anymore. Probably because I'm doing alot of C++ studying lately.

Anyway down to business. 'State' is just a user defined class that refers to a state in good old USA. The code is vaguely based around population comparison. The guy wants to make an STL function object that he can pass to the std::sort function. As far as I know tho - std::sort requires a function pointer or function object to be passed to it as one of it's arguments. Well obviously Popless isn't a function it's a struct. But it does make use of the function call operator overload and I am told that this means it will make the grade required to be used in an std::sort operation.

Odd really, I can only assume then that std::sort only looks to see how the argument passed to it is triggered/initiated. It musn't actually look to see what exact details belong to the argument being passed to check to see if it's a correct entity or anything, it just wants to know how to start it. Makes sense really I suppose, I guess anything that was too inquisitive might cause a load of unnecessary operations to occur at a low level. If I'm talking bull please correct me - my education would benefit ;o)

Anyway - std::sort doesn't panic when passed a structure - provided it has an operator () overload defined it it. Ok fair enough. Now noting that popLess (get's confusing here, this is the actual function call and it is supposed to be inline) is the function actually triggered, the author states that this will retain its inline and thus its speed benefit. I understand this would NOT happen if popLess were simply used in the std::sort call, and you would lose the inline benefits of it.

I believe the author made this entire function object purely for this purpose. So basically after all that stuff I have these questions:

1)Does calling it this way preserve the inline speed of popLess?

2)Does the fact that all this extra insulating code exists - the Popless struct stuff - negate any speed benefit of preserving the inline property of popless?

3)Apparently the shrewd choice of inheriting from std::binary function adds the benefit of being able to find out the argument and return types of the function object Popless. Hmmm.... ok but why would we want to do that? The author says it's something to do with allowing capability for other users. Why don't those other users just go and check out the definition in the IDE and write their code to suit?

MY brain is very sore so I'm going to stop there. I hope that's made some sense, I am trying as hard as I can to understand it, but it does feel a bit new and I wonder if I'm talking buloney sometimes. Anyway - thanks in advance for any help anyone can offer ;o)

Share this post


Link to post
Share on other sites
Quote:

I also know Zahlman hates me calling it the STL but I don't know how else to refer to it ;o)

The Standard C++ Library, or SC++L for short.

Quote:

Odd really, I can only assume then that std::sort only looks to see how the argument passed to it is triggered/initiated.

Exactly. std::sort() simply tries to call its Predicate parameter as a function. If it works (because it is a function pointer or a function object/functor) then it is happy. If not you'll probably get a nasty and verbose template error message.

Quote:

Does calling it this way preserve the inline speed of popLess?

Probably. With modern compilers "inline" is a hint, it doesn't guarantee inlining. A good C++ optimiser will inline the function if it thinks it should.

Quote:

Does the fact that all this extra insulating code exists - the Popless struct stuff - negate any speed benefit of preserving the inline property of popless?

Quite the opposite. Using a wrapper for this avoids passing the predicate as a function pointer, which typically inhibits further optimisations.

Quote:

Apparently the shrewd choice of inheriting from std::binary function adds the benefit of being able to find out the argument and return types of the function object Popless. Hmmm.... ok but why would we want to do that? The author says it's something to do with allowing capability for other users. Why don't those other users just go and check out the definition in the IDE and write their code to suit?

Because the "users" are often other template functions - not actual people. These use the typedefs provided by std::binary_function to declare variables of the correct type. In C++0x, the "auto" and "decltype" will alleviate the need for this - but that won't be common for some time.

Share this post


Link to post
Share on other sites
I understood your reply perfectly thanks so much!

I'd like to ask one thing if anyone can help. Regarding the way the std::binary_function is used. Here's the code again:

struct Popless : public std::binary_function<State, State, bool> {
bool operator () (const State &a, const State &b) const
{return popLess (a, b);} // popLess is an inline function
};


I notice that you don't have to have any form of pre-existing user defined version of std::binary_function here to just inherit from. Sounds fair enough given it's an STL thing, but this is actually a user defined/altered entity because of the:

std::binary_function<State, State, bool>

parts in bold. So this cleary doesn't run like a typical inherit would. I am quite surprised how flexible this is. That you can go straight to a user defined argument list without having to pre-declare like you normally do with typical user defined classes.

To be honest I'm getting a bit confused I think I need to do some research into this. I have some questions if anyone can help before I go googling:

1)Exactly what does the way the argument list is set-up mean? Or is this even an argument list at all? I'm referring to the:

<State, State, bool>

part. Is this maybe not an argument list and is just declaring what type of variables the inherited struct will use for preparation purposes? It's the only part I'm still a little confused about.

Share this post


Link to post
Share on other sites
<State, State, bool> is the template argument list for the std::binary_function<> template. This is similar to how for std::vector<> you supply a template argument for different kinds of vectors (ex: std::vector<int>, std::vector<double>, etc.). However, std::binary_function<> takes three template arguments: one for the first argument's type, one for the second argument's type and one for the return value type.

Share this post


Link to post
Share on other sites
It is a template argument list. Are you familiar with templates? This is just inheritance from a template class with three type parameters.

Looking at the definition might help:

template <class Arg1, class Arg2, class Result>
struct binary_function {
typedef Arg1 first_argument_type;
typedef Arg2 second_argument_type;
typedef Result result_type;
};

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this