一, 问题
在常规的函数调用中, 通常是直接看到函数的定义并调用该函数, 所以, 函数调用传递的是值还是引用是根据函数声明来决定的. 但是, 在 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.0\libstdc++-v3\include\tr1\functional
- 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\n", x);
- tr1::bind(foo, tr1::ref(x))();
- printf("x %d\n", x);
- }
- tsecer@harry: g++ usetr1.cpp
- tsecer@harry: ./a.out
- x 1
- x 10
来源: http://www.bubuko.com/infodetail-2978878.html