【stl】从一段不能编译的代码说起

Paradin 2008-04-01 05:05:58

下面这个程序只要改一个字符便能通过编译

#include <iostream>
#include <functional>
using namespace std;

class A
{
/*...*/
int i;
public:
A(int k) : i(k) {}
int get() const {return i;}
};

int isLess(const A& a, int c)
{
return a.get() < c;
}

int main()
{
A a(34);
cout << bind2nd(ptr_fun(isLess), 45)(a) << endl;
return 0;
}

虽然知道原因了 我总觉得这里有点混,又比如你在程序中使用greater<HugeClass>, 不知情的可能试图用HugeClass的引用 HugeClass& 来实例化此模板因为stl实现的greater<T>实际上是用const T&来传递参数的因此不必担心效率,反而程序员亲自操心的话会报错。我不知道这里面有什么原则准则来规范模板参数的含义没有?虽然可以用指针来代替引用消除上面的问题但我想如果编译器把 Type&&&... (一个或多个&)都解析为Type& 应该也不是什么难事。

请教一下意见谢谢!!!
...全文
257 16 打赏 收藏 举报
写回复
16 条回复
切换为时间正序
请发表友善的回复…
发表回复
Paradin 2008-04-06
  • 打赏
  • 举报
回复
我在博客写了个总结:http://blog.csdn.net/Paradin/archive/2008/04/06/2254830.aspx 指教下。
结贴了。
Paradin 2008-04-05
  • 打赏
  • 举报
回复
[Quote=引用 13 楼 zenny_chen 的回复:]
嗯。Paradin转贴的内容还是不错的。虽然代码长了些,不过要解决这个去除引用的问题也只能如此了吧?
[/Quote]

也许可以自己重写替代库里的bind2nd和ptr_fun吧。我想六楼那个是为了能直接使用stl的ptr_fun那样写。导致代码逻辑不算太优雅。
Paradin 2008-04-05
  • 打赏
  • 举报
回复
其实那样传递参数时还是多进行了一次对象复制操作,和程序员的意图还是有偏差吧?
谢谢大家的讨论。过小阵子结贴
Vitin 2008-04-02
  • 打赏
  • 举报
回复
从某种意义上说,taodm是对的。

不过,能够自己实现某些库功能的人,是值得尊敬的。他们或许(极有可能)不是不知道如何使用标准库、准标准库,而是想要探究其中的奥秘,做到自己实现;并且,将自己的成果与人分享。我在想,自己是否能够做到这一点,我敬佩这样的人。

PS:6楼的代码貌似有一个小错误,修改一下:
template <class T>
typename FunTraits <T>::RET_TYPE ref_fun(T fun,
typename FunTraits <T>::PARAM1_TYPE p1,
typename FunTraits <T>::PARAM2_TYPE p2)
{
return fun(p1,p2); // 缺少了一个return
}
瑕不掩瑜,学习6楼的代码。
taodm 2008-04-02
  • 打赏
  • 举报
回复
不在于boost内部怎么实现。
有现成标准库、准标准库不知道使用的,都远称不上牛人。
Paradin 2008-04-02
  • 打赏
  • 举报
回复
那说不定boost内部就是他那样的
taodm 2008-04-02
  • 打赏
  • 举报
回复
汗,这水平还称牛人。
cout < < boost::bind(isLess, _1, 45)(a) < < endl;
Paradin 2008-04-02
  • 打赏
  • 举报
回复
牛人完美解决方案:

Little Fatty
你好,问一个问题。
C++中参数为引用类型的函数貌似不能被ptr_fun包装成函数对象?

Little Fatty
我想知道这是不是只是ms的问题
#include <iostream>
#include <functional>
using namespace std;

int isLess(const int& a, int c)
{
return a < c;
}

int main()
{
int a;
cin >> a;
cout << bind2nd(ptr_fun(isLess), 45)(a) << endl;
return 0;
}

vs的编译器会报错:error C2529: '_Left' : reference to reference is illegal

原因好像是实现bind2nd的返回模板类binder2nd的时候
result_type operator()(argument_type& _Left) const 他擅自将其推导出的类型(argument_type = const int&) 变为引用,因而就成了const int&&, 于是报上述错误

Thanks!

Little Fatty 16:46:19
想了一下,如果全部由程序员指定的话, 那么就得写类似greater<const SomeClass&>这样的代码,可能显得不大高级吧。感觉C++在这里有点混

XXXX 01:20:59
你的分析是对的,这个不是ms的问题,gcc也是这样。这需要写一个函数适配器,如下:(我有空也会把答案贴到我的群空间中)

XXXX 01:22:53
答案如下,我写了一个去函数引用的适配器,将有引用参数的函数适配成没引用的函数,即将引用"&"去掉,外部接口是ref_fun,结果如下:

XXXX 01:22:55
#include <cstdlib>
#include <iostream>
#include <functional>
using namespace std;

template<class T>
struct FunTraits
{};

template<class T,class U,class RET>
struct FunTraits<RET(*)(T,U)>
{
typedef RET RET_TYPE;
typedef T PARAM1_TYPE;
typedef U PARAM2_TYPE;
};

template<class T,class U,class RET>
struct FunTraits<RET(*)(T&,U)>
{
typedef RET RET_TYPE;
typedef T PARAM1_TYPE;
typedef U PARAM2_TYPE;
};

template<class T,class U,class RET>
struct FunTraits<RET(*)(T,U&)>
{
typedef RET RET_TYPE;
typedef T PARAM1_TYPE;
typedef U PARAM2_TYPE;
};

template<class T,class U,class RET>
struct FunTraits<RET(*)(T&,U&)>
{
typedef RET RET_TYPE;
typedef T PARAM1_TYPE;
typedef U PARAM2_TYPE;
};

template<class T>
typename FunTraits<T>::RET_TYPE ref_fun(T fun,
typename FunTraits<T>::PARAM1_TYPE p1,
typename FunTraits<T>::PARAM2_TYPE p2)
{
fun(p1,p2);
}

template<class T>
struct Fun_Adp
{
typedef typename FunTraits<T>::RET_TYPE RET_TYPE;
typedef typename FunTraits<T>::PARAM1_TYPE PARAM1_TYPE;
typedef typename FunTraits<T>::PARAM2_TYPE PARAM2_TYPE;
typedef RET_TYPE (*FPOINTER_TYPE)(PARAM1_TYPE,PARAM2_TYPE);

static T m_pfun;
explicit Fun_Adp(T pfun)
{
m_pfun = pfun;
}

static RET_TYPE SimFun(PARAM1_TYPE p1, PARAM2_TYPE p2)
{
return m_pfun(p1,p2);
}

FPOINTER_TYPE ToFunPtr()
{
return SimFun;
}
};

template<class T>
T Fun_Adp<T>::m_pfun;

template<class T>
Fun_Adp<T> ref_fun_adp(T pFun)
{
return Fun_Adp<T>(pFun);
}

template<class T>
typename Fun_Adp<T>::FPOINTER_TYPE ref_fun(T pFun)
{
return ref_fun_adp(pFun).ToFunPtr();
}

int isLess(const int& a, int& c)
{
return a < c;
}

int main()
{
int a;
cin >> a;

cout << bind2nd(ptr_fun(ref_fun(isLess)), 45)(a) << endl;
system("pause");

return 0;
}
zenny_chen 2008-04-02
  • 打赏
  • 举报
回复
嗯。Paradin转贴的内容还是不错的。虽然代码长了些,不过要解决这个去除引用的问题也只能如此了吧?
  • 打赏
  • 举报
回复
你说的这种情况的确存在,这需要写代码的人对底层的stl有一定的理解并且能从编译器给出的有限信息中快速定位到问题并解决之
C++0x中已经有引用的引用,所以你说的这种情况在C++0x中将不复存在
abupie 2008-04-02
  • 打赏
  • 举报
回复
[Quote=引用 9 楼 taodm 的回复:]
不在于boost内部怎么实现。
有现成标准库、准标准库不知道使用的,都远称不上牛人。
[/Quote]
牛人很多种的, 技术牛人也分很多种的, 知识广的牛人, 技术深的牛人.等等.
比如不懂技术只懂赚钱的人, 是不是牛人呢? 我看来,他们也是牛人.
Paradin 2008-04-01
  • 打赏
  • 举报
回复
/
Paradin 2008-04-01
  • 打赏
  • 举报
回复
是这么一回事吧!
Vitin 2008-04-01
  • 打赏
  • 举报
回复
标准库的类或函数在C++标准(《ISO/IEC 14882:2003(E)》)中都有原型描述,按照标准来就可以了。

对于第二个问题,将Type&&&...解析为Type&当然很容易实现,但是很难说这是合理的。因为它可能会忽视掉很多错误的代码(比如一个笔误),所以当一个特性并非很有用时,语言是不会轻易增加它的。
只要记住,在标准库里,模版参数无须加引用就可以了。事实上,当程序员试图加引用来提高一个模版的效率时,他是需要知道模版是怎样定义的(否则他怎么能确定加引用可以提高效率呢),那么他就不应该犯这样的错误。好在,编译器总会对此报错,那么它就不是一个大问题。
Paradin 2008-04-01
  • 打赏
  • 举报
回复
?
taodm 2008-04-01
  • 打赏
  • 举报
回复
看《STL源码剖析》或者《The C++ Standard Library》
相关推荐
发帖
C++ 语言

6.3w+

社区成员

C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
帖子事件
创建了帖子
2008-04-01 05:05
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下