给出一个问题:如果有一个函数f和一个对象x,现在我们希望在x上调用f,而在x的成员函数之外,为了执行这个调用操作,C++提供了3种不同的语法:
f(x);//语法#1: f是一个non-member-func
x.f();//语法#2: f是一个member-func,x是一个对象or对象的引用
px->f();//语法#3: f是一个member-func,且px是一个指向对象x的指针
由于存在这3种不同的语法,因此在使用STL的一些组件时,不免会产生一些语法上的冲突。而ptr_func、mem_fun、mem_func_ref这三个函数(在头文件<functional>中)的出现就是用来掩盖C++语言中的一个内在语法不一致的问题!(下面请继续看我写的 test codes 以便于理解该term所要讲述的内容)
test_codes:
#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
#include<list>
using namespace std;
class Widget {
public:
string m_res;
Widget(const string& s):m_res(s){}
inline void reviseRes(const string& s) { this->m_res = s; };
void testfunc() {
if (m_res.length() > 3)reviseRes(string("success!"));//string的长度超过3记作"成功"!
else reviseRes(string("failure!"));//string的长度不超过3记作"失败"!
}
};
//现在有一个可用于测试Widget对象的非成员函数test
void test(Widget& w) {
if (w.m_res.length() > 3)w.reviseRes(string("success!"));
else w.reviseRes(string("failure!"));
}
int main(void) {
//...
return 0;
}
test1:
//语法1:直接传入对象x来调用非成员函数(non-mem-func)
for_each(vw.begin(), vw.end(), test);
//or
for_each(vw.begin(), vw.end(), ptr_fun(test));
test1_result:

test2:
//语法2:使用Widget对象x的成员函数(mem-func)来传入for_each遍历的每一个对象!
//for_each(vw2.begin(), vw2.end(), (&Widget::testfunc));//错误示范!×
for_each(vw2.begin(), vw2.end(), mem_fun_ref(&Widget::testfunc));//正确示范!√
test2_result:
错误示范result:

正确示范result:

test3:
//语法3:使用Widget对象x的成员函数(mem-func)来传入for_each遍历的每一个对象的指针!
//for_each(lpw.begin(), lpw.end(), (&Widget::testfunc));//错误示范1!×
//for_each(lpw.begin(), lpw.end(), mem_fun_ref(&Widget::testfunc));//错误示范2!×
for_each(lpw.begin(), lpw.end(), mem_fun(&Widget::testfunc));//正确示范!√
test3_result:
错误示范1result:

错误示范2result:

正确示范result:

total_test_codes:
#include<iostream>
#include<vector>
#include<algorithm>
#include<functional>
#include<list>
using namespace std;
class Widget {
public:
string m_res;
Widget(const string& s):m_res(s){}
inline void reviseRes(const string& s) { this->m_res = s; };
void testfunc() {
if (m_res.length() > 3)reviseRes(string("success!"));//string的长度超过3记作"成功"!
else reviseRes(string("failure!"));//string的长度不超过3记作"失败"!
}
};
//现在有一个可用于测试Widget对象的非成员函数test
void test(Widget& w) {
if (w.m_res.length() > 3)w.reviseRes(string("success!"));
else w.reviseRes(string("failure!"));
}
int main(void) {
vector<Widget> vw{ Widget("lzfy"),Widget("tjr") ,Widget("lyfy") };
cout << "before using object call test(non-mem-fun):" << endl;
for_each(vw.begin(), vw.end(), [](const Widget& w) {
cout << w.m_res << "\t";
});
cout << endl;
//语法#1
for_each(vw.begin(), vw.end(), (test));
//or
//for_each(vw.begin(), vw.end(), ptr_fun(test));
cout << "after using object call test(non-mem-fun):" << endl;
for_each(vw.begin(), vw.end(), [](const Widget& w) {
cout << w.m_res << "\t";
});
cout << endl;
cout << "--------------------------------" << endl;
//语法#2
vector<Widget> vw2{ Widget("lzfy"),Widget("tjr") ,Widget("lyfy") };
cout << "before using object call test(mem-fun):" << endl;
for_each(vw2.begin(), vw2.end(), [](const Widget& w) {
cout << w.m_res << "\t";
});
cout << endl;
for_each(vw2.begin(), vw2.end(), mem_fun_ref(&Widget::testfunc));
cout << "after using object call test(mem-fun):" << endl;
for_each(vw2.begin(), vw2.end(), [](const Widget& w) {
cout << w.m_res << "\t";
});
cout << endl;
cout << "--------------------------------" << endl;
//语法#3
list<Widget*> lpw;
//当然,我这里仅仅用裸指针来do测试而已。实际coding时STL容器要存储指针类型对象时,都要优先使用shared_ptr<T>来存对应之对象指针!
lpw.push_back(new Widget("lzfy"));
lpw.push_back(new Widget("tjr"));
lpw.push_back(new Widget("lyfy"));
cout << "before using pointer-to-object call test(mem-fun):" << endl;
for_each(lpw.begin(), lpw.end(), [](const Widget* w) {
cout << w->m_res << "\t";
});
cout << endl;
for_each(lpw.begin(), lpw.end(), mem_fun(&Widget::testfunc));
cout << "after using pointer-to-object call test(mem-fun):" << endl;
for_each(lpw.begin(), lpw.end(), [](const Widget* w) {
cout << w->m_res << "\t";
});
cout << endl;
return 0;
}
运行结果:

总结:
本条款意在告诉我们,当
①将一个non-member-func(非成员函数)传递给一个STL组件(多数时候是一个STL算法,而你需要传入一个Func/Pred)时,为了避免你为此感到困惑,你要在函数前加一个ptr_fun(do一些类型定义的工作,这个工作细节是干啥的不重要,重要的是你要会用,from Effective STL Term40)
注:如果你不知道什么时候该使用ptr_fun,什么时候不该使用,那么你大可以再每一次将函数传递给一个STL组件时总是使用它!STL不会在意,且这么做也不会带来代码运行时性能上的损失!最糟糕的仅仅是,当别人阅读你的代码时,如果看到了不必要的ptr_fun,可能会皱眉头(别人很可能会看不懂你为啥要这么干,一头雾水)!即:这么干你的代码可读性会降低,但我个人认为鲁棒性是会提高的!
②将一个member-func(成员函数)传递给一个STL组件(多数时候是一个STL算法,而你需要传入一个Func/Pred)时,并且对应之STL容器中存储的是class object(类的对象)时,你要在函数前加一个mem_fun_ref(do一些类型定义的工作,这个工作细节是干啥的不重要,重要的是你要会用,from Effective STL Term40)
③将一个member-func(成员函数)传递给一个STL组件(多数时候是一个STL算法,而你需要传入一个Func/Pred)时,并且对应之STL容器中存储的是pointer to class object(指向类对象的指针)时,你要在函数前加一个mem_fun(do一些类型定义的工作,这个工作细节是干啥的不重要,重要的是你要会用,from Effective STL Term40)
若不按照Term41的3个总结经验写这类代码,则你的代码根本就不可能通过编译!so,老老实实遵守本条款的经验哈~
75

被折叠的 条评论
为什么被折叠?



