&&&&*** C++ 2011(MS版本)中 std::bind 的源代码,模版推导出现问题了???--------

hzhxxx 2013-08-02 05:47:32
世界钢铁技术头号王国卢森堡


c:\program files\microsoft visual studio 11.0\vc\include\functional
里面,调用 std::bind,里面实际上是调用了 _Bind 的构造函数,构造了一个
_Bind类返回了,我们跟踪类 _Bind 的定义,他是依赖

_VARIADIC_EXPAND_0X(_CLASS_BIND, , , , )
这个宏来定义的。

// for 0-X args
#define _VARIADIC_EXPAND_0X(FUNC, X1, X2, X3, X4) \
_VARIADIC_EXPAND_0(FUNC, X1, X2, X3, X4) \
_VARIADIC_EXPAND_1X(FUNC, X1, X2, X3, X4)

#define _VARIADIC_EXPAND_1X(FUNC, X1, X2, X3, X4) \
_VARIADIC_EXPAND_1(FUNC, X1, X2, X3, X4) \
_VARIADIC_EXPAND_2X(FUNC, X1, X2, X3, X4)

#if _VARIADIC_MAX == 5
#define _VARIADIC_EXPAND_2X _VARIADIC_EXPAND_25

#define _VARIADIC_EXPAND_25(FUNC, X1, X2, X3, X4) \
_VARIADIC_EXPAND_2(FUNC, X1, X2, X3, X4) \
_VARIADIC_EXPAND_3(FUNC, X1, X2, X3, X4) \
_VARIADIC_EXPAND_4(FUNC, X1, X2, X3, X4) \
_VARIADIC_EXPAND_5(FUNC, X1, X2, X3, X4)

最终展开到类定义一级,应应是6个定义,索引从 0~5
#define _VARIADIC_EXPAND_0(FUNC, X1, X2, X3, X4) \
FUNC(_TEM_LIST0, _PAD_LIST0, _RAW_LIST0, , X1, X2, X3, X4)

#define _VARIADIC_EXPAND_1(FUNC, X1, X2, X3, X4) \
FUNC(_TEM_LIST1, _PAD_LIST1, _RAW_LIST1, _COMMA, X1, X2, X3, X4)

#define _VARIADIC_EXPAND_1(FUNC, X1, X2, X3, X4) \
FUNC(_TEM_LIST1, _PAD_LIST1, _RAW_LIST1, _COMMA, X1, X2, X3, X4)

#define _VARIADIC_EXPAND_2(FUNC, X1, X2, X3, X4) \
FUNC(_TEM_LIST2, _PAD_LIST2, _RAW_LIST2, _COMMA, X1, X2, X3, X4)
....
#define _VARIADIC_EXPAND_5(FUNC, X1, X2, X3, X4) \
FUNC(_TEM_LIST5, _PAD_LIST5, _RAW_LIST5, _COMMA, X1, X2, X3, X4)

类似这样的定义,应是6个定义,索引从 0~5
_CLASS_BIND(_TEM_LIST0, _PAD_LIST0, _RAW_LIST0, _COMMA, X1, X2, X3, X4)
_CLASS_BIND(_TEM_LIST1, _PAD_LIST1, _RAW_LIST1, _COMMA, X1, X2, X3, X4)
......
_CLASS_BIND(_TEM_LIST5, _PAD_LIST5, _RAW_LIST5, _COMMA, X1, X2, X3, X4)


template<bool _Forced, class _Ret, class _Fun, _MAX_CLASS_LIST> class _Bind;

#define _CLASS_BIND( \
TEMPLATE_LIST2, PADDING_LIST2, LIST2, COMMA, X1, X2, X3, X4) \
template<bool _Forced, \
class _Ret, \
class _Fun COMMA LIST2(_CLASS_TYPEX)> \
class _Bind<_Forced, _Ret, _Fun, \
LIST2(_TYPEX) COMMA PADDING_LIST2(_NIL_PAD)> \
: public _Add_result_type<_Forced, \
_Has_result_type< \
typename decay<_Fun>::type>::type::value, \
_Ret, \
typename decay<_Fun>::type> \
{...省略里面的定义和实现};


我们展开宏_VARIADIC_EXPAND_0X,实际上应该得到6 个类似
FUNC(_TEM_LIST2, _PAD_LIST2, _RAW_LIST2, _COMMA, X1, X2, X3, X4)
的定义,这样,最后展开,我发现这样会出现6个 _Bind 的模版类定义,
关键的是前面的 template<arg>,里面的 arg 参数个数还不一样,这样应该编译通不过的吧


#define _VARIADIC_EXPAND_ALT_05(FUNC, X1, X2, X3, X4) \
_VARIADIC_EXPAND_ALT_0(FUNC, X1, X2, X3, X4) \
_VARIADIC_EXPAND_ALT_1(FUNC, X1, X2, X3, X4) \
_VARIADIC_EXPAND_ALT_2(FUNC, X1, X2, X3, X4) \
_VARIADIC_EXPAND_ALT_3(FUNC, X1, X2, X3, X4) \
_VARIADIC_EXPAND_ALT_4(FUNC, X1, X2, X3, X4) \

#define _VARIADIC_EXPAND_ALT_2(FUNC, X1, X2, X3, X4) \
FUNC(_TEM_LIST2, _PAD_LIST2, _RAW_LIST2, _COMMA, X1, X2, X3, X4)

我们以 _CLASS_BIND(_TEM_LIST2, _PAD_LIST2, _RAW_LIST2, _COMMA, X1, X2, X3, X4) 为例展开

template<bool _Forced, class _Ret, class _Fun COMMA _RAW_LIST2(_CLASS_TYPEX)> class _Bind<_Forced, _Ret, _Fun,
_RAW_LIST2(_TYPEX) COMMA _PAD_LIST2(_NIL_PAD)> : public _Add_result_type<_Forced,
_Has_result_type<typename decay<_Fun>::type>::type::value, _Ret, typename decay<_Fun>::type> {...省略里面的定义和实现};


#define _RAW_LIST2(MAP) MAP(0) _COMMA MAP(1)

#define _PAD_LIST2 _PAD_LIST0_3
#define _PAD_LIST0_3(MAP) MAP(0) _COMMA MAP(1) _COMMA MAP(2) _COMMA MAP(3)

#define _CLASS_TYPEX(NUM) class _VAR_TYPEX(NUM)
#define _VAR_TYPEX(NUM) _Vx ## NUM ## _t

#define _TYPEX(NUM) _VAR_TYPEX(NUM)

#define _NIL_PAD(NUM) _Nil
// STRUCT _Nil
struct _Nil
{ // empty struct, for unused argument types
};

根据上面的定义,推导出来的结果应该是
_RAW_LIST2(_CLASS_TYPEX) -> _CLASS_TYPEX(0),_CLASS_TYPEX(1) ->
class _VAR_TYPEX(0),class _VAR_TYPEX(1) -> class _Vx0_t,class _Vx1_t

_RAW_LIST2(_TYPEX) -> _TYPEX(0),_TYPEX(1)->_VAR_TYPEX(0),_VAR_TYPEX(1)->_Vx0_t,_Vx1_t

_PAD_LIST2(_NIL_PAD)->_NIL_PAD(0),_NIL_PAD(1)->_Nil,_Nil

这个就是携带两个参数的_Bind 类模版的完整定义
template<bool _Forced, class _Ret, class _Fun, class _Vx0_t,class _Vx1_t>
class _Bind<_Forced, _Ret, _Fun, _Vx0_t,_Vx1_t,_Nil,_Nil> : public _Add_result_type<_Forced,
_Has_result_type<typename decay<_Fun>::type>::type::value, _Ret, typename decay<_Fun>::type> {...省略里面的定义和实现};

很容易推导不携带参数和多个参数的定义
template<bool _Forced, class _Ret, class _Fun>
class _Bind<_Forced, _Ret, _Fun> : public _Add_result_type<_Forced,
_Has_result_type<typename decay<_Fun>::type>::type::value, _Ret, typename decay<_Fun>::type> {...省略里面的定义和实现};

template<bool _Forced, class _Ret, class _Fun, class _Vx0_t,class _Vx1_t,class _Vx2_t>
class _Bind<_Forced, _Ret, _Fun, _Vx0_t,_Vx1_t,_Vx2_t,_Nil,_Nil> : public _Add_result_type<_Forced,
_Has_result_type<typename decay<_Fun>::type>::type::value, _Ret, typename decay<_Fun>::type> {...省略里面的定义和实现};

问题出来了,模版参数个数不一致,不能通过编译啊
template<bool _Forced, class _Ret, class _Fun, _MAX_CLASS_LIST> class _Bind;
template<bool _Forced, class _Ret, class _Fun> class _Bind<_Forced, _Ret, _Fun> : public _Add_result_type<_Forced, _Has_result_type<typename decay<_Fun>::type>::type::value, _Ret, typename decay<_Fun>::type> {...省略里面的定义和实现};
template<bool _Forced, class _Ret, class _Fun, class _Vx0_t,class _Vx1_t> class _Bind<_Forced, _Ret, _Fun, _Vx0_t,_Vx1_t,_Nil,_Nil> : public _Add_result_type<_Forced, _Has_result_type<typename decay<_Fun>::type>::type::value, _Ret, typename decay<_Fun>::type> {...省略里面的定义和实现};
template<bool _Forced, class _Ret, class _Fun, class _Vx0_t,class _Vx1_t,class _Vx2_t> class _Bind<_Forced, _Ret, _Fun, _Vx0_t,_Vx1_t,_Vx2_t,_Nil,_Nil> : public _Add_result_type<_Forced, _Has_result_type<typename decay<_Fun>::type>::type::value, _Ret, typename decay<_Fun>::type> {...省略里面的定义和实现};
...全文
303 19 打赏 收藏 转发到动态 举报
AI 作业
写回复
用AI写文章
19 条回复
切换为时间正序
请发表友善的回复…
发表回复
hzhxxx 2013-08-07
  • 打赏
  • 举报
回复
//专门化(偏特化)模版类 template<class _Fun> class _Bind_A<_Fun> { private: _Fun m_v; public: _Bind_A(_Fun v):m_v(v) { // } const std::string cat() { std::stringstream str; str<<m_v<<endl; return str.str(); } };
hzhxxx 2013-08-07
  • 打赏
  • 举报
回复
/* prepare.cpp 演示了阅读 STL 原代码的一些预备知识 1. 模版以及模版的偏特化,是 STL 源代码里面用的很多的技术, 有效的解决了支持不同参数个和类型的问题; 2. 各式各样的宏定义,用于定义构造函数,重载函数实现,需要不断的展开, 这个也是平时代码很少见的特征 */ //标明是使用 MS版本,本处使用 VS2012 update 4 #include "stdafx.h" #include <iostream> #include <sstream> using namespace std; #define join_1(a,b) a+b #define join_2(a,b) ab #define join_3(a,b) a##b #define join_4(a,b) v_ ## a ## _ ## b ## _t void test_micro() { //演示了宏的一些定义 int x = 11,y = 22; std::cout<<join_1(x,y)<<endl; //注意,是输出了变量的值 int ab = 333; std::cout<<join_2("x","y")<<endl; int xy = 999; std::cout<<join_3(x,y)<<endl; int v_11_22_t = 9999; std::cout<<join_4(11,22)<<endl; int v_x_y_t = 99999; std::cout<<join_4(x,y)<<endl; } //声明一个模版类,容许最少有一个模板参数,最多有4个模板参数, //该最多模版参数的空模版没有实现,所以其他的就只能最多有3个模版参数了 //最后一个class = Null_class,无参数名称,是标明是不命名模板参数, //Null_class 是一个空结构体,定义是: struct Null_class{}; //STL 原代码里面定义了一个 _Nil struct Null_class{}; template<class _Fun,class v0_t = Null_class,class v1_t = Null_class,class = Null_class> class _Bind_A;
ri_aje 2013-08-06
  • 打赏
  • 举报
回复
引用 16 楼 hzhxxx 的回复:
[quote=引用 15 楼 ri_aje 的回复:] 那里多了一个,你是说 class _Fun 吗?
是的。。。[/quote] 那是 callable signature 的类型。比如 void f (int); bind(f,1); _Fun 就是 void(int)。 这样设计可以确保 bind 至少要捆绑在一个可以调用的实体上。 要是把所有参数都开成可选的,那 bind() 也是一个合法的表达式了,可是却没什么用,而且很迷惑。 事实上模板形参都可选,bind() 也未必合法,只不过会在某个实例化的地方生成编译错误,更难排查。
hzhxxx 2013-08-06
  • 打赏
  • 举报
回复
引用 15 楼 ri_aje 的回复:
那里多了一个,你是说 class _Fun 吗?
是的。。。
ri_aje 2013-08-05
  • 打赏
  • 举报
回复
那里多了一个,你是说 class _Fun 吗?
hzhxxx 2013-08-05
  • 打赏
  • 举报
回复
引用 12 楼 hzhxxx 的回复:
问题出来了,模版参数个数不一致,不能通过编译啊
周六在家里折腾一个下午,知道了这点技术。 但还是有些不明白,为啥模版里面的参数需要多一个。。。??
hzhxxx 2013-08-05
  • 打赏
  • 举报
回复
//声明一个模版类,容许最少有一个模板参数,最多有3个模板参数, //最后一个class = _Nil,无参数类型,是标明是不可用模板参数, //_Nil是一个空结构体,定义是: struct _Nil{}; template<class _Fun,class v0_t = _Nil,class v2_t = _Nil,class = _Nil> class _Bind_A; //专门化(偏特化)模版类 template<class _Fun> class _Bind_A<_Fun> { // }; template<class _Fun,class v0_t> class _Bind_A<_Fun,v0_t> { // }; //专门化(偏特化)模版类,同时演示了模板继存的方式 template<class _Fun,class v0_t,class v1_t> class _Bind_A<_Fun,v0_t,v1_t> : public std::binary_function<v0_t,v1_t,bool> { // }; 上面的能通过编译,实际上就是应用了上面的技术
hzhxxx 2013-08-05
  • 打赏
  • 举报
回复
问题出来了,模版参数个数不一致,不能通过编译啊
ri_aje 2013-08-03
  • 打赏
  • 举报
回复
手头没有 vs2011,翻了一下 2010 版的,用的不一样的技术。 关于模板实参个数的问题,把 class _Bind primary template 定义中的 _MAX_CLASS_LIST 发上来看一下,如果这个宏每次编译的时候扩展成带默认实参的形参列表,个数不一致问题就不存在。
icosagon 2013-08-02
  • 打赏
  • 举报
回复
估计你的模板参数过多,又没有定义所需要宏引起的
icosagon 2013-08-02
  • 打赏
  • 举报
回复
微软的c++编译器现在不支持可变模板参数,现在是用宏模拟的可变模板参数,而且还得用宏定义控制参数个数,建议别看实现了,就是一陀垃圾代码,能把人看眼花,好像微软的vs2013才会支持可变模板参数。
max_min_ 2013-08-02
  • 打赏
  • 举报
回复
散分么?楼主呢??
zhctj159 2013-08-02
  • 打赏
  • 举报
回复
楼主到底想问啥?
taodm 2013-08-02
  • 打赏
  • 举报
回复
楼主到底想问啥?
大尾巴猫 2013-08-02
  • 打赏
  • 举报
回复
这。。。。嵌套了几层啊,看得眼都花了
  • 打赏
  • 举报
回复
max_min_ 2013-08-02
  • 打赏
  • 举报
回复
好长 好长
hzhxxx 2013-08-02
  • 打赏
  • 举报
回复
沙发这么快就没有了啊!!!!!!%%%%%
漫步者、 2013-08-02
  • 打赏
  • 举报
回复

65,187

社区成员

发帖
与我相关
我的任务
社区描述
C++ 语言相关问题讨论,技术干货分享,前沿动态等
c++ 技术论坛(原bbs)
社区管理员
  • C++ 语言社区
  • encoderlee
  • paschen
加入社区
  • 近7日
  • 近30日
  • 至今
社区公告
  1. 请不要发布与C++技术无关的贴子
  2. 请不要发布与技术无关的招聘、广告的帖子
  3. 请尽可能的描述清楚你的问题,如果涉及到代码请尽可能的格式化一下

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