大家指点:《c++标准程序库》一书中,p126---p127中的疑问

littleroy 2003-07-24 06:32:16
函数:for_each(col1.begin(), col1.end(), PrintIntI())
其中,PrintInt()为访函数,一种类的对象

而书中说写出for_each的算法如下:
template<class Iterator, class Operation>
Operation for_each(Iterator act, Iterator end, Operation op)
{
while (act!=end){op(*act);++act;}
return op;

}
可见,for_each函数的第三参数为一种类的对象(访函数)

而后书中有:for_each函数可接受,一般函数为第三参数如:
void add10(int& elem){
elem += 10;
}
void f1(){
for_each(col1.begin(), col1.end(), add10)
}


请问,这是怎么回事呢??for_each的原码算法显示,它的第三参数不是一种类的对象吗,为何能接受一般函数为参数??????
请指教,,
...全文
33 9 打赏 收藏 转发到动态 举报
写回复
用AI写文章
9 条回复
切换为时间正序
请发表友善的回复…
发表回复
短歌如风 2003-07-31
  • 打赏
  • 举报
回复

Cowboy22所说的是typename的另一种用法,而我说的是在定义模板参数时不要再使用class,而应该改用typename。
关于Cowboy22所说的typename的这种用法的讨论在amstrongest的一个贴子中进行过。
短歌如风 2003-07-31
  • 打赏
  • 举报
回复

事实上我们为函数对象定义operator()操作符就是为了让模板可以同时使用函数对象和函数指针,因为我们可以为对象定义operator(),然后用op()的方法去调用,但我们没有办法为函数指针定义一个成员函数,用op.execute()去执行。就象我们为std::string(准确地说是std::basic_string)定义了operator<,然后可以用同样的代码对整型数组和字符串数组排序一样。
事实上,我一向认为C++中的操作符重载缺点多于优点,但当template从保留关键字的阴影中走出来后,操作符重载也获得了新生——它使我们可以用同一段模板代码操作自己定义的类和语言定义的简单类型。
Cowboy22 2003-07-31
  • 打赏
  • 举报
回复
typename 和class有时是不能互换的,之所以加入这个keyword 是为了解决在某些地方,编译器辨别表达式存在二义性的问题。比如:
template <class T>
void f(T)
{
T::g(t);
T::g t;
}
在f(T)没有具现化之前,从字面上看, T::g可以有两种意义:要么是类型,T::g(t)被解析成constructor call,要么是类型的静态成员函数,T::g(t)被解析成调用T类的静态成员函数g(),这个时候编译器无法判断词法的正确性,所以编译器总是假设T::g不是类型。

如果要告诉编译器T::g是一种类型,就用到typename:
template<class T>
void f(T)
{
typename T::g t;

}
MatrixCpp 2003-07-30
  • 打赏
  • 举报
回复
上面的代码没有问题

template<class Iterator, class Operation>
Operation for_each(Iterator act, Iterator end, Operation op)
{
while (act!=end){op(*act);++act;}
return op;

}

void add10(int& elem){
elem += 10;
}
void f1(){
for_each(col1.begin(), col1.end(), add10)
}

add10相当与op传递进去,add10就是一个函数指针,所以这个时候的class Operation 中的Operation 相当与申明了一个(*fptr)()类型。而add10就是这个类型(函数指针)的对象

楼上已经有人说了,类型是个非常广义的概念,决不只是int,class A等等这些。
simouse 2003-07-30
  • 打赏
  • 举报
回复
听课
短歌如风 2003-07-25
  • 打赏
  • 举报
回复
补充一下:这时生成的代码中for_each的实例化函数的声明类似于:
typedef void (*Fun) (int&);
Fun for_each(list<int>::iterator act, list<int>::iterator end, Fun Op);

短歌如风 2003-07-25
  • 打赏
  • 举报
回复
template<class Iterator, class Operation>这种写法是旧的风格,现在通常都写成template<typename Iterator, typename Operation>,就是为了更容易理解。

事实上,在上述模板参数中,虽然写的是class,但它是可以用任何类型的,不一定是class。比如:
template <class T>
T min(T a, T b)
{
return a > b? b :a;
}
这时你可以用int类型(并不是一个class)实例化这个模板函数:
int x = min(3,4)。

在楼主贴的代码中,for_each(col1.begin(), col1.end(), add10)这一调用最后一个参数类型是void (*)(int&),这里编译器会自动用这个类型代替模板参数中的Operation对模板进行实例化,结果相当于(由于楼主没有说col1的类型,我假设它是一个list<int>):
for_each<list<int>::iterator, void(*)(int&)>(col1.begin(), col1.end(), add10);

可以看到,模板参数只有两种,一种是“类型参数”,一种是“常量参数”。“类型参数”的旧的写法就是class,但其实可以用任意类型,所以现在把它写成typename更合适。而常量参数的写法则是具体常量类型如int、void*等。

ThinkX 2003-07-24
  • 打赏
  • 举报
回复
读书不可太死板,要理解为什么,比如为什么用仿函数,而不用其他手段呢,多思考则必有收获。
ThinkX 2003-07-24
  • 打赏
  • 举报
回复
函数是不保存状态的,任何外部变量都会从参数输入。
仿函数是保存状态的,如果需要的变量通过参数输入不足,可以放在类中,这也是仿函数存在的意义。

既然你自己都说了,第三个参数是仿函数,那么必然是模仿函数的工作,所以函数当然也可以作为参数了。

所以,如果for_each的第三个参数中的所有处理都是通过参数传入的,那么就用函数;如果仅仅通过传入参数信息不足,那么就用仿函数,在其构造时传出其他参数并在对象中保存。

24,854

社区成员

发帖
与我相关
我的任务
社区描述
C/C++ 工具平台和程序库
社区管理员
  • 工具平台和程序库社区
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
暂无公告

试试用AI创作助手写篇文章吧