C++.Coding.Standards.1918.Rules.Guidelines [Electronic resources] نسخه متنی

اینجــــا یک کتابخانه دیجیتالی است

با بیش از 100000 منبع الکترونیکی رایگان به زبان فارسی ، عربی و انگلیسی

C++.Coding.Standards.1918.Rules.Guidelines [Electronic resources] - نسخه متنی

Herb Sutter, Andrei Alexandrescu

| نمايش فراداده ، افزودن یک نقد و بررسی
افزودن به کتابخانه شخصی
ارسال به دوستان
جستجو در متن کتاب
بیشتر
تنظیمات قلم

فونت

اندازه قلم

+ - پیش فرض

حالت نمایش

روز نیمروز شب
جستجو در لغت نامه
بیشتر
لیست موضوعات
توضیحات
افزودن یادداشت جدید


Discussion


First, function objects are easy to make adaptable, and always should be (see Item 89). Even if you already have a function, sometimes you have to wrap it in

ptr_fun or

mem_fun anyway to add adaptability. For example, you have to do this in order to build up more complex expressions using binders (see also Item 84):

inline bool IsHeavy( const Thing& ) {

/*…*/ }
find_if( v.begin(), v.end(),

not1( IsHeavy ) );

// error: isn't adaptable

The workaround is to insert

ptr_fun (or, for a member function,

mem_fun or

mem_fun_ref ):

inline bool IsHeavy( const Thing& ) {

/*…*/ }
find_if( v.begin(), v.end(),

not1( ptr_fun<Thing,void>( IsHeavy ) ) );

// ok: now it's adaptable

Aside:
Yes, it's a pain that here you need to explicitly specify

ptr_fun 's template arguments. This is another drawback to using functions. Briefly, the reason the template arguments are needed is that

ptr_fun deduces the argument and return types exactly and creates a

pointer_to_unary_function , which in turn helpfully tries to add another

& , and references to references are not currently allowed by ISO C++. There are ways in which

ptr_fun could, and probably should, be fixed so as to strip top-level

const and

& from non-pointer parameter and return types (see Item 89), but it doesn't do that today.Item 89), which is adaptable from the get-go without special syntax:

struct IsHeavy : unary_function<Thing, bool> {
bool operator()( const Thing& ) const {

/*…*/ }
};
find_if( v.begin(), v.end(),

not1( IsHeavy() ) );

// ok: adaptable

More importantly, you need a function object, not a function, to specify comparers for associative containers. This is because it's illegal to instantiate a template type parameter with a function type directly:

bool CompareThings( const Thing&, const Thing& );
set<Thing,

CompareThings > s;

// error

Instead, you need:

struct CompareThings : public binary_function<Thing,Thing,bool> {
bool operator()( const Thing&, const Thing& ) const;
};
set<Thing,

CompareThings > s;

// ok

Finally, there is also an efficiency benefit. Consider this familiar algorithm:

template<typename Iter, typename Compare>
Iter find_if( Iter first, Iter last,

Compare comp );

If we pass a function as the comparer to

find_if

inline bool

Function ( const Thing& ) {

/*…*/ }
find_if( v.begin(), v.end(),

Function );

we're actually passing a reference to

Function . Compilers rarely inline such function calls (except as part of whole-program analysis, which is still a relatively recent feature on popular compilers), even when as above the function is declared

inline and is visible while compiling the

find_if call. And, as noted, functions aren't adaptable.

If we pass a function object as the comparer to

find_if

struct FunctionObject : unary_function<Thing, bool> {
bool

operator() ( const Thing& ) const {

/*…*/ }
};
find_if( v.begin(), v.end(),

FunctionObject() );

we're passing an object that typically has an (implicitly or explicitly) inline

operator() function. Compilers have routinely inlined such calls since C++'s Bronze Age.

Note: This is not to encourage premature optimization (see Item 8), but to discourage premature pessimization (see Item 9). If you already have a function, go ahead and pass a pointer to the function (unless you have to wrap it with

ptr_fun or

mem_fun anyway). But if you're writing a new piece of code for use as an argument to an algorithm, prefer writing the extra boilerplate to make it a function object.


/ 521