一、问题
在常规的函数调用中,通常是直接看到函数的定义并调用该函数,所以,函数调用传递的是值还是引用是根据函数声明来决定的。但是,在std::tr1中的bind函数本身是为了完成不同格式参数的适配,所以函数调用处看到的内容和真正的函数执行位置看到的内容并不相同。
简单的说,这里可以简单的模拟下bind的实现方法
tsecer@harry: cat tr1_ref.cpp
template<typename T, typename F>
int foo(F f, T t)
{
return f(t);
}
typedef int(*funtype)(int &, int);
struct adaptor
{
funtype m_fun;
int m_arg;
adaptor(funtype f, int y)
:m_fun(f), m_arg(y)
{}
int operator()(int x)
{
return m_fun(x, m_arg);
}
};
int bar(int &x, int y)
{
return x + y;
}
int main()
{
int x =1, y =2;
adaptor a(bar, y);
foo(a, x);
}
tsecer@harry: g++ tr1_ref.cpp
tsecer@harry:
在上面的例子中,adaptor a(bar, y) 语句生成的局部变量就相当于是 tr1::bind生成的中间变量类型,可以看到的是,虽然bar函数声明自己的第一个参数是一个引用类型,但是在adaptor类中,它依然只是把它作为一个值保存起来,进而在调用bar函数的时候,传入的是一个复制的值而不是引用,所以导致最终并不是希望的结果。
二、gcc的std库如何解决这个问题
1、引入特殊ref类型
template<typename _Tp>
class reference_wrapper
: public _Reference_wrapper_base<typename remove_cv<_Tp>::type>
{
// If _Tp is a function type, we can't form result_of<_Tp(...)>,
// so turn it into a function pointer type.
typedef typename _Function_to_function_pointer<_Tp>::type
_M_func_type;
_Tp* _M_data;
public:
typedef _Tp type;
explicit reference_wrapper(_Tp& __indata): _M_data(&__indata)
{ }
reference_wrapper(const reference_wrapper<_Tp>& __inref):
_M_data(__inref._M_data)
{ }
reference_wrapper&
operator=(const reference_wrapper<_Tp>& __inref)
{
_M_data = __inref._M_data;
return *this;
}
operator _Tp&() const
{ return this->get(); }
_Tp&
get() const
{ return *_M_data; }
#define _GLIBCXX_REPEAT_HEADER <tr1/ref_wrap_iterate.h>
#include <tr1/repeat.h>
#undef _GLIBCXX_REPEAT_HEADER
};
// Denotes a reference should be taken to a variable.
template<typename _Tp>
inline reference_wrapper<_Tp>
ref(_Tp& __t)
{ return reference_wrapper<_Tp>(__t); }
在其基类reference_wrapper中,其拷贝构造函数有一个这样的声明
typedef _Tp type;
explicit reference_wrapper(_Tp& __indata): _M_data(&__indata)
{ }
也就是用匹配类型给变量初始化的时候,把其地址保存在自己的_M_data中,而不是保存自己独立的一份数据,所以可以达到引用的效果。
2、在参数提取中
特例化参数为reference_wrapper类型的_Mu模版,从而在参数提取时调用__arg.get(),这个时候就可以返回引用类型。
gcc-4.1.0libstdc++-v3include r1functional
template<typename _Arg,
bool _IsBindExp = is_bind_expression<_Arg>::value,
bool _IsPlaceholder = (is_placeholder<_Arg>::value > 0)>
class _Mu;
/**
* @if maint
* If the argument is reference_wrapper<_Tp>, returns the
* underlying reference. [TR1 3.6.3/5 bullet 1]
* @endif
*/
template<typename _Tp>
class _Mu<reference_wrapper<_Tp>, false, false>
{
public:
typedef _Tp& result_type;
/* Note: This won't actually work for const volatile
* reference_wrappers, because reference_wrapper::get() is const
* but not volatile-qualified. This might be a defect in the TR.
*/
template<typename _CVRef, typename _Tuple>
result_type
operator()(_CVRef& __arg, const _Tuple&) const volatile
{ return __arg.get(); }
};
三、使用tr1库看下效果
可以看到,如果不使用 tr1::ref封装x,那么虽然bind的函数参数使用的是引用,但是最终的结果依然没有修改到传入的参数x。
tsecer@harry: cat usetr1.cpp
#include <tr1/functional>
#include <stdio.h>
using namespace std;
typedef tr1::function<void(int &)> ff;
void foo(int &x)
{
x *= 10;
}
int main()
{
int x = 1;
tr1::bind(foo, x)();
printf("x %d
", x);
tr1::bind(foo, tr1::ref(x))();
printf("x %d
", x);
}
tsecer@harry: g++ usetr1.cpp
tsecer@harry: ./a.out
x 1
x 10