原文来自以下链接,自己查询到此,无时间翻译亦怕翻译出错,故将原文贴于此,以备自己回顾。
written by Herb Sutter
original site: http://collaboration.cmc.ec.gc.ca/science/rpn/biblio/ddj/Website/articles/CUJ/2003/0308/cujcexp0308sutter/

Copyright (c) 2003 Herb Sutter

The function facility, recently adopted by the C++ standards committee, provides a generalized way of working with arbitrary functions when all you know (or need to know) is their signature. In fact, as it turns out, you don’t even need to know the target function’s exact signature – any target function with a compatible signature, meaning one where the parameter and return types are appropriately covertible, will work just fine.

Last time [1], I gave an overview of tuple types, one of the first two library extensions that were approved in October 2002 for the standard library extensions technical report (the “Library TR”). In this article, I’ll cover the other: Doug Gregor’s proposal for polymorphic function object wrappers. [2] Note that both of these facilities come directly from the Boost project [3], an important (and free) collection of C++ libraries available for you to download and try out today.

In brief, the function facility provides a generalized way of working with arbitrary functions when all you know (or need to know) is their signature. In fact, as it turns out, you don’t even need to know the target function’s exact signature - any target function with a compatible signature, meaning one where the parameter and return types are appropriately convertible, will work just fine.

A Motivating Example

Functions and functionlike things are everywhere: C++ has member functions, static member functions, and nonmember (namespace scope) functions, and on top of that it has functors (objects having an overloaded operator()) that can be invoked as though they were functions. (I’m going to use the term “functors” for the latter, instead of the alternative “function objects,” to avoid confusion with “function objects” which means objects of the function library we’re discussing.)

Calling a plain old function is easy:

// Example 1(a): Calling a nonmember function
//
string AppendA( string s ) { return s + 'a'; }string test = "test";// Making an immediate call is easy:
//cout << AppendA( test ) << endl;             // immediate call

When it comes to using a function, however, it turns out that what and when to call aren’t always decided at the same time. That is, a program may determine early on exactly which function needs to be called, long before it is ready to actually make the call; as noted in [2], a prime and common example is registering a callback function. In cases like that, we routinely know to squirrel away a reference to the function, typically by using a function pointer:

// Example 1(a), continued
//
// Making a delayed call involves storing a pointer
// (or reference) to the function:
//
typedef string (*F)( string );
F f = &AppendA;                               // select function...
// ... later...
cout << f( test ) << endl;                    // ... make delayed call

This is handy and pretty flexible, in that f can be made to point to any nonmember function that takes and returns a string.

But of course the above method doesn’t work for all functions and functionlike things. For example, what if we instead wanted to do this with a member function? “That’s easy,” someone might say, “we just have to store a member function pointer instead of a free function pointer, and then use it appropriately.” That’s true, and it means we would write code that looks something like this:

// Example 1(b): Calling a member function
//
class B {
public:virtual string AppendB( string s ) { return s + 'b'; }
};class Derived : public B {string AppendB( string s ) { return s + "bDerived"; }
};string test = "test";
B b;
Derived der;// Making an immediate call is easy:
//
cout << b.AppendB( test ) << endl;            // immediate call// Making a delayed call involves storing a pointer
// (or reference) to the member function, and requires
// some appropriate object to call it on:
//
typedef string (B::*F)( string );
F f = &B::AppendB;                            // select function...
// ... later...
cout << (b.*f)( test ) << endl;               // ... make delayed call
cout << (der.*f)( test ) << endl;             // ... another delayed call,//   virtual dispatch works

That’s fine, and this works, but note that it’s already considerably less flexible than in Example 1(a). Why? Because the f in Example 1(b) can only be made to point to member functions of class B that take and return a string; this f can’t be used to point to a member function of any other class, even if it otherwise has the right signature. While we’re at it, note one more limitation: This f also doesn’t carry around with it the knowledge of which B object to invoke. We can get rid of this last limitation, remembering the object on which the call should be made, by using the standard function binders:

// Example 1(b), continued: Alternative, using binders
//
typedef binder1st<mem_fun1_t<string,B,string> > F2;
F2 f2 = bind1st( mem_fun( &B::AppendB ), &b );
// ... later...
cout << f2( test ) << endl;                   // ... make delayed callf2 = bind1st( mem_fun( &B::AppendB ), &der );
// ... later...
cout << f2( test ) << endl;                   // ... another delayed call,//  virtual dispatch works

The type of F2 is unfortunately ugly, and it still is limited to binding to member functions of class B having an exact signature.

Of course, if the thing we wanted to call was actually a functor object, then none of the above options would work, this despite the fact that for immediate calls the functor can indeed be used seamlessly as though it were a function:

// Example 1(c): Calling a functor
//
class C {
public:string operator()( string s ) { return s + 'c'; }
};string test = "test";
C c;// Making an immediate call is easy:
//
cout << c( test ) << endl;                    // immediate call// Making a delayed call is trickier. There's no easy way without
// fixing the type of the object as a C, such as by taking a C&...:
//
typedef C& F;
F f = c;                                      // select functor...
// ... later...
cout << f( test ) << endl;                    // ... make delayed call// ...or by arranging for C to inherit from some common base class
// with a virtual operator() and then taking a pointer or reference
// to that base.

That covers nonmember functions, member functions, and functors. Finally, on a note that applies to all of these, what if you have a function (or functor) that you could call directly just fine, but the parameter types, although compatible, aren’t quite identical? Consider a variant of Example 1(a):

// Example 1(d): 1(a) with slightly different types
//
// A little class just to get some conversions happening
class Name {
public:Name( string s ) : s_( s ) { }string Get() const { return s_; }private:string s_;
};// Stubbed-in function to demonstrate varying
// parameter and return types:
const char* AppendD( const Name& s ) {static string x;x = s.Get() + 'd';return x.c_str();
}string test = "test";// Making an immediate call is still easy:
//
cout << AppendD( test ) << endl;              // immediate call// But the typedef from Example 1(a) no longer works,
// even though the rest of the call is unchanged:
//
typedef string (*F)( string );
F f = &AppendD;                               // error: type mismatch
// ... later (in our dreams)...
cout << f( test ) << endl;                    // ... never get here

Enter function

What if we had a facility that let you form a “function pointer” to any function or functionlike thing? That would be genuinely useful. Enough people think it would be genuinely useful, in fact, that many have gone off and written their own using C++’s powerful templates, and today several versions of such facilities exist; see [2] for some references. And function in the draft standard library technical report is just such a facility, so “That’s great, these already exist,” one might think. “Why write yet another one for the library technical report?” Because these existing facilities are interesting (good), useful (even better), and not always portable or compatible (oops).

When something is genuinely and widely useful, and people keep reinventing the same wheel in slightly different variations, we have a prime candidate for standardization. After all, just imagine what the world would be like if, instead of one standard std::string, every vendor supplied their own incompatible string (or CString, or RWString, or blah_string) type with different names and different interfaces and features to learn and relearn on every system. Those of you who’ve been around C++ since about 1995 or earlier don’t need to imagine the horror of lots of incompatible string types; you’ve lived it. Even today with a standard string, we still have some of those variant vendor-specific string types floating around, but at least there are far fewer of them than there used to be. Similarly now with generalized function pointers, the standards committee felt it was time we had a single general-purpose fucntion wrapper that we can rely on portably, and the one that the committee has chosen to bless is function.

The function template takes a function signature as its only required template parameter. (Following the practice of much of the rest of the standard library, it also takes an optional allocator template parameter, which I’m going to ignore henceforth.) Its declaration looks like this:

template< typename Function, typename Allocator = std::allocator<void> >
class function;

How do you pass a function signature as a template parameter? The syntax may seem slightly odd at first, but once you’ve seen it it’s just what you’d think:

function< string (string) > f;                // can bind to anything that// takes and returns a string

Any function or functor that is callable with the specified signature can be bound to the function object. In particular, the function object above can bind to all of the functions shown in the earlier examples, as demonstrated in Example 1(e):

// Example 1(e): One function<> to bring them all,
// and flexibly to bind them
//
// This code uses the same class and function definitions
// from Examples 1(a) through (d)
//
string test = "test";
B b;
Derived der;
C c;function<string (string)> f;                  // can bind to anything that// takes and returns a string
f = &AppendA;                                 // bind to a free function
cout << f( test ) << endl;f = bind1st( mem_fun( &B::AppendB ), &b );
cout << f( test ) << endl;  // bind to a member function
f = bind1st( mem_fun( &B::AppendB ), &der );
cout << f( test ) << endl;                    // with virtual dispatchf = c;                                        // bind to a functor
cout << f( test ) << endl;f = &AppendD;                                 // bind to a function with a
cout << f( test ) << endl;                    // different but compatible signature

“Wow,” you say? Wow indeed. Note in particular the implicit conversions going on in the last case, for which we didn’t even have legal code in Example 1(d) - AppendD’s actual parameter and return types are not string, but can be converted from and to a string, respectively, and that’s good enough for function< string (string) >.

Another Motivating Example: Higher-Order Functions

What if you want to write a function that returns a function? Today, you have two main choices: First, you could return a pointer to function, in which case that’s all you get; in particular, you can’t easily strap on state information, or a member function prebound to some variable that can carry the state. Second, you could return a full-fledged functor, but in that case there’s a lot of boilerplate code that you’ll reproduce each time. Instead, as Gregor demonstrates in [2], you can use a generalized function as follows.
Let’s say you want to write an arithmetic_operation function whose purpose in life is to return a functor that can perform some appropriate operation on two int s, so that the calling code can store it and later invoke it at the appropriate time. The catch: It has to decide at runtime what kind of operation will be needed; perhaps you want to add, or perhaps you want to subtract, or multiply, or divide, and so on. The arithmetic_operation function should decide which is needed, create an appropriate functor (e.g, std::plus< >, std::minus< >, etc.), and return it - but then how do you write the return type of arithmetic_operation, when the functors being returned all have different types? What you need is a return type that can be constructed from any of those types of objects – in this case, a function< int (int, int)>:

// Example 2: Higher-order functions
// (reproduced from [2], section Ib)
//
function<int (int x, int y)> arithmetic_operation(char k)
{switch (k) {case '+': return plus<int>();case '-': return minus<int>();case '*': return multiplies<int>();case 'c:\www.cuj.com/': return divides<int>();case '%': return modulus<int>();default: assert(0);}
}

Note what’s going on here: At runtime, the arithmetic_operation function decides what kind of operation will be needed. Since each type can be bound to a function< int (int, int)>, we’re done; no fuss, no muss.

More To Come: Observer, Comparisons, and Multicast, Oh My!

I have quite a bit more to say about function - more, as it turns out, than fits well in this overview column. While writing this overview, I inadvertently also stumbled straight into an analysis of a generalized Observer pattern implementation using function that has already turned into a (very long) article in its own right: the next Sutter’s Mill column [5] (also published here in the CUJ Experts Forum). I’ve devoted that upcoming column to a deeper analysis of function. In that column, we will see how function is a powerful tool for generalizing important design patterns (e.g., Observer), but we will also see how that same analysis highlights two limitations in function’s design. One of the limitations is significant and fixable, and the other is less significant because there is an easy workaround. These two limitations are:

  • Comparison: The only significant limitation of function is that function objects can’t be compared, not even for equality. As I will argue in depth in the upcoming companion column, there are compelling reasons why function should provide at least equality comparison (== and !=). This is possible and usefully implementable, albeit with a ripple effect into the standard function binders (e.g., std::mem_fun).
  • Multicast: Another, but fortunately less significant, limitation of function is that a function object can only be bound to one thing at a time. The reason this is not too significant a limitation is that there’s a perfectly good workaround: It’s possible to build a multi_function on top of function pretty easily and effectively, and I will motivate and demonstrate that as well in the companion column, including a sample (i.e., working, but rough) implementation.

Summary

For all of its power, function isn’t all that expensive to use. Gregor notes: “A boost::function<> object is 3 pointers large (it could be optimized to 2 in most cases), and requires one extra indirect call (through a function pointer) for each invocation of the boost::function<> object. If one is careful, you can make sure that no big arguments are copied by the bridge to further reduce the penalty for large objects, although I haven’t implemented this. (It’s a simple application of, e.g., boost::call_traits to the parameter list).” [6]

Further, it’s again a testament to the power of C++ that a function that works with all functions and functors can be implemented purely as a library facility, without any extensions to the existing standard C++ core languages. Don’t try this at home in less-flexible languages.

In the next The New C++ column, I’ll give a trip report on the April 2003 C++ standards meeting, held in Oxford, UK. The big news: Whereas in the October 2002 meeting the first two extensions were adopted for the standard library extensions technical report, in April no fewer than ten more were added. What were they, and which ones might you particularly care about? Stay tuned.

References

[1] H. Sutter. "Tuples" (C/C++ Users Journal, 21(6), June 2003).[2] D. Gregor. "A Proposal to add a Polymorphic Function Object Wrapper to the Standard Library," ISO/ANSI C++ standards committee paper (ISO/IEC JTC1/SC22/WG21 paper N1402, ANSI/NCITS J16 paper 02-0060).[3] www.boost.org[4] H. Sutter. More Exceptional C++ (Addison-Wesley, 2002). An earlier version of this material is available online at www.gotw.ca/gotw/057.htm.[5] H. Sutter. "Generalizing Observer" (C/C++ Users Journal, 21(9), September 2003). [should be published within 2-3 weeks of this article][6] D. Gregor, private communication

About the Author

Herb Sutter (< www.gotw.ca>) is convener of the ISO C++ standards committee, author of the acclaimed books Exceptional C++ and More Exceptional C++, and one of the instructors of The C++ Seminar (< www.gotw.ca/cpp_seminar>). In addition to his independent writing and consulting, he is also C++ community liaison for Microsoft.

Generalized Function Pointers相关推荐

  1. 成员函数指针与高性能的C++委托 (Member Function Pointers and the Fastest Possible C++ Delegates)...

    标准C++中没有真正的面向对象的函数指针.这一点对C++来说是不幸的,因为面向对象的指针(也叫做"闭包(closure)"或"委托(delegate)")在一些 ...

  2. 换一种思路看function pointers指针函数)

    今天跟一个朋友聊到函数指针的问题,发现其实很多人不懂得如何解读一个函数指针的声明.其实有一个非常好用的法则,但我观察到很多视频或者文章里并没有提到,只是单纯告诉你他举的例子如何解读,换一个例子又不知道 ...

  3. C++11 FAQ中文版:std::function 和 std::bind

    std::function 和 std::bind 标准库函数bind()和function()定义于头文件中(该头文件还包括许多其他函数对象),用于处理函数及函数参数.bind()接受一个函数(或者 ...

  4. 成员函数指针与高性能的C++委托(三)

    委托(delegate) 和成员函数指针不同,你不难发现委托的用处.最重要的,使用委托可以很容易地实现一个Subject/Observer设计模式的改进版[GoF, p. 293].Observer( ...

  5. 成员函数指针与高性能的C++委托(下篇)

    成员函数指针与高性能的C++委托(下篇) 撰文:Don Clugston (接中篇) 委托(delegate) 和成员函数指针不同,你不难发现委托的用处.最重要的,使用委托可以很容易地实现一个Subj ...

  6. [C++]各编译器对C++11的支持比较

    各编译器对C++11的支持比较 转载请注明出处为KlayGE游戏引擎,本文的永久链接为http://www.klayge.org/?p=2154 在KlayGE首次引入C++11特性之后,我顺便调研了 ...

  7. C++11 FAQ中文版

    C++11 FAQ中文版 http://www.chenlq.net/cpp11-faq-chs http://www.stroustrup.com/C++11FAQ.html Morgan Stan ...

  8. 在C++中实现委托(Delegate)

    在C++中实现委托(Delegate) 标签: C++设计模式 2016-03-18 21:04  494人阅读  评论(1)  收藏  举报   分类: C/C++(166)   设计模式(28)  ...

  9. 对象引用与托管指针(object references and managed pointers)

    C/C++中的指针是一种非常灵活而强大的引用机制,但同时也非常脆弱,稍有不慎,就会出错. Java完全摈弃了指针的概念,而代之以对象引用(object reference),基本上消灭了由指针而导致的 ...

最新文章

  1. 你曾经是那手握烙铁的少年
  2. 【PAT乙级】1086 就不告诉你 (15 分)
  3. 《Python数据可视化编程实战》—— 1.2 安装matplotlib、Numpy和Scipy库
  4. 个位百位AS3实现经典算法(二) 水仙花数
  5. 《线性代数及其应用》笔记-第三章
  6. 【口令破解】远程口令破解和本地口令破解(crunch 字典工具和hydra工具)
  7. (二)以太网与WiFi协议
  8. 数据结构韩顺平版(2)
  9. 链路聚合、Trunk、端口绑定和捆绑简析
  10. 数据防泄密·工控安全保障方案
  11. oracle xla相关,【EBS】XLA_GLT表的清理
  12. 法国语言学校c1,法国留学的语言要求是什么?
  13. 素数回文(打表到文件里面)
  14. 正交匹配追踪算法OMP(Orthogonal Matching Pursuit)
  15. 华为证书HCIE怎么样?考华为HCIE有用吗?
  16. 线上支付,出款和收款
  17. 音频编码之opus(一)
  18. 学习算法笔记(12)
  19. 愤怒的老王,每天都想暗杀一个同事...
  20. 用vscode写markdown的正确姿势

热门文章

  1. 攻防世界 misc 打野
  2. 一键爬取王者荣耀全皮肤高清图片【方法一】
  3. A股上市公司财报披露时间
  4. MoviePy - 中文文档4-MoviePy实战案例-重新构建15世纪舞蹈视频
  5. lsdyna如何设置set中的node_list_为 Windows PowerShell 设置 User Alias (命令别名)
  6. C++中的dynamic_cast和dynamic_pointer_cast
  7. 计算机打字教程ppt,计算机打字基础教学.ppt
  8. 怎么修改PDF文档背景
  9. MATLAB 中的调用C语音DLL 库
  10. 045:魔兽世界三(开战) 程序设计实习MOOC / 程序设计与算法(三)测验题汇总(2020春季) 已经结束