std::ref() 与 &
引言
之前因为调整样式把博客园的样式毁了,所以一直在自己的另一个博客上更新,有兴趣的可以去观望一下:http://blog.yunlambert.top/最近还是把博客园拾起来吧。。。。。
最近看到一个多线程代码如下:
typedef unsigned long long ULL;
void accumulator_function(const std::vector<int> &v, ULL &acm,
unsigned int beginIndex, unsigned int endIndex)
{
acm = 0;
for (unsigned int i = beginIndex; i < endIndex; ++i)
{
acm += v[i];
}
}
int main()
{
ULL acm1 = 0;
ULL acm2 = 0;
std::vector<int> v = { 1,2,3,4,5,6,7,8,9,10 };
std::thread t1(accumulator_function, std::ref(v),
std::ref(acm1), 0, v.size() / 2);
std::thread t2(accumulator_function2, std::ref(v),
std::ref(acm2), v.size() / 2, v.size());
t1.join();
t2.join();
std::cout << acm1 << "+" << acm2 << "=" << acm1 + acm2 << std::endl;
return 0;
}
其中创建线程的部分使用了 std::thread t1(accumulator_function2, std::ref(v), std::ref(acm1), 0, v.size() / 2);,对应的函数实现为void accumulator_function(const std::vector<int> &v, ULL &acm, unsigned int beginIndex, unsigned int endIndex),该函数的参数为一个vector引用、一个计算结果acm的引用和记录vector首末位置的index。其中的std::ref()之前在C++ Primer中看过,感觉应该和&差不多吧,但是既然这样为什么仍然需要用ref转换成引用形式呢?立刻把std::ref()全部删了,重新运行,结果报错了......有点意思,可以探究一波。
探究过程
引用的例子首先列举一个:
void fun(vector<int> &a, int i)
{
a[i] = 20;
}
int main()
{
std::vector<int> v = { 1,1,1,1,1,1 };
for (auto x : v)
cout << x << ' ';
cout << endl;
fun(v, 3);
for (auto x : v)
cout << x << ' ';
}
// Output:
// 1 1 1 1 1 1
// 1 1 1 20 1 1
如果是普通引用的话,只需要调用fun(v, 3)就行了,为什么在例子中使用了fun(std::ref(v),..)这种形式呢?说明std::ref()和&是不一样的么?写个例子看一下:
#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
int main()
{
int x = 5;
std::cout << boolalpha << is_same<int&, decltype(ref(x))>::value;
return 0;
}
输出答案果然是false!那么如果std::ref()返回的不是对象的引用,返回的是什么?查一下手册可以发现:函数模板 ref 与 cref 是生成 std::reference_wrapper 类型对象的帮助函数,它们用模板实参推导确定结果的模板实参。所以std::ref()返回的实际上是一个reference_wrapper而不是T&,可以从一个指向不能拷贝的类型的对象的引用生成一个可拷贝的对象。 std::reference_wrapper 的实例是对象(它们可被复制或存储于容器),但它们能隐式转换成 T& ,故能以之为以引用接收底层类型的函数的参数。
修改一下上面的例子,看看结果:
#include <iostream>
#include <type_traits>
#include <functional>
using namespace std;
int main()
{
int x = 5;
std::cout << boolalpha << is_same<int&, decltype(ref(x).get())>::value;
return 0;
}
变为true了。reference_wrapper与&并不一样,但是利用get()函数就是&类型。但是为什么在多线程那个例子中要使用std::ref()呢?
原因是,考虑了函数式编程(如std::bind)在使用时,是对参数直接拷贝,而不是引用。具体可以参照这一句话:std::reference_wrapper 用于按引用传递对象给 std::bind 或 std::thread 的构造函数
还是通过代码理解一下:
#include <functional>
#include <iostream>
void f(int& n1, int& n2, const int& n3)
{
std::cout << "In function: " << n1 << ' ' << n2 << ' ' << n3 << '\n';
++n1; // increments the copy of n1 stored in the function object
++n2; // increments the main()'s n2
}
int main()
{
int n1 = 1, n2 = 2, n3 = 3;
std::function<void()> bound_f = std::bind(f, n1, std::ref(n2), std::cref(n3));
n1 = 4;
n2 = 5;
n3 = 6;
std::cout << "Before function: " << n1 << ' ' << n2 << ' ' << n3 << '\n';
bound_f();
std::cout << "After function: " << n1 << ' ' << n2 << ' ' << n3 << '\n';
}
得到的答案为:
Before function: 4 5 6
In function: 1 5 6
After function: 4 6 6
上述代码在执行std::bind后,在函数f()中n1的值仍然是1,n2和n3改成了修改的值。说明std::bind使用的是参数的拷贝而不是引用。C++11的设计者认为bind默认应该采用拷贝,如果使用者有需求,加上std::ref()即可。同理std::thread也是这样。
结论
std::ref只是尝试模拟引用传递,并不能真正变成引用,在非模板情况下,std::ref根本没法实现引用传递,只有模板自动推导类型时,ref能用包装类型reference_wrapper来代替原本会被识别的值类型,而reference_wrapper能隐式转换为被引用的值的引用类型,但是并不能被用作&类型。
而回到刚开始的那个多线程代码,thread的方法传递引用的时候,我们希望使用的是参数的引用,而不是浅拷贝,所以必须用ref来进行引用传递。
std::ref() 与 &的更多相关文章
- 为什么C++11引入了std::ref?
C++本身有引用(&),为什么C++11又引入了std::ref? 主要是考虑函数式编程(如std::bind)在使用时,是对参数直接拷贝,而不是引用.如下例子: #include <f ...
- C++11 std::ref使用场景
C++本身有引用(&),为什么C++11又引入了std::ref(或者std::cref)? 主要是考虑函数式编程(如std::bind)在使用时,是对参数直接拷贝,而不是引用.如下例子: # ...
- c++11之为什么C++11引入了std::ref?
C++本身有引用(&),为什么C++11又引入了std::ref? 主要是考虑函数式编程(如std::bind)在使用时,是对参数直接拷贝,而不是引用.如下例子: #include <f ...
- c++11多线程---std::ref和std::cref
std::ref和std::cref 解释 std::ref 用于包装按引用传递的值. std::cref 用于包装按const引用传递的值. 为什么需要std::ref和std::cref ...
- std::ref和std::cref使用(转载)
转载于:https://blog.csdn.net/lmb1612977696/article/details/81543802 std::ref和std::cref 解释: std::ref 用于包 ...
- std::bind与std::ref, why and how
首先解释下为什么有时候需要bind. 我们可以用bind从函数T add(T a, T b)造出个inc()来,即把b写死为1.这个例子本身比较傻,但有不傻的应用. template<typen ...
- boost和std中的thread的引用参数
boost 1.60.0 先上代码: #include <boost/thread.hpp> #include <iostream> void add(int &i) ...
- 用C++11的std::async代替线程的创建
c++11中增加了线程,使得我们可以非常方便的创建线程,它的基本用法是这样的: void f(int n); std::thread t(f, n + 1); t.join(); 但是线程毕竟是属于比 ...
- std::thread
std::shared_ptr<std::thread> m_spThread; m_spThread.reset(new std::thread(std::bind(&GameS ...
随机推荐
- FFMPEG-AVFilter研究
FFMPEG中的libswscale是做像素转换的,但是对于一些复杂的操作,比如添加水印等,这个库就不行了,这时候就要说一下另外一个AVFilter.AVFilter完全可以替代libswscale的 ...
- 使用 Anthem.NET 的常见回调(Callback)处理方式小结
在 Anthem.NET 中,通过 XmlHttp 或 XmlHttpRequest 组件对服务器端所作的一次无刷新调用(通常是异步模式),称为一个回调(Callback). 本文内容是对 Anthe ...
- Poj 2299 Ultra-QuickSort(归并排序求逆序数)
一.题意 给定数组,求交换几次相邻元素能是数组有序. 二.题解 刚开始以为是水题,心想这不就是简单的冒泡排序么.但是毫无疑问地超时了,因为题目中n<500000,而冒泡排序总的平均时间复杂度为, ...
- js强加方法
(function () { Array.prototype.removeItem=function (item) { var index=this.indexOf(item); if(index!= ...
- 杂项:zabbix(WEB界面的提供分布式系统监视以及网络监视功能)
ylbtech-杂项:zabbix(WEB界面的提供分布式系统监视以及网络监视功能) zabbix(音同 zæbix)是一个基于WEB界面的提供分布式系统监视以及网络监视功能的企业级的开源解决方案.z ...
- 一个有关Golang变量作用域的坑
转自:http://tonybai.com/2015/01/13/a-hole-about-variable-scope-in-golang/ 临近下班前编写和调试一段Golang代码,但运行结果始终 ...
- USB插拔检测程序
一.手动添加ON_WM_DEVICECHANGE()消息 二.添加头文件#include <Dbt.h> 三.定义设备的GUID static const GUID GUID_DEVINT ...
- 进击的菜鸟问题1(设置checkbox全选)
问题:设置页面标签属性时,常常使用jquery.attr("","");在设置checkbox属性时,会出现操作设置checkbox属性后,无法清除,导致第二次 ...
- service的生命周期以及两种service的差异
可以看到,两种service的生命周期都相对简单,有一点不同的是,Intentservice每次调用的时候都执行onstartcommand,而boundservice一旦启动了之后,就不会每次执行o ...
- ServerSocket01
ServerSocket表示服务端套接字:我们首先来看下其中的一个构造器: public ServerSocket(int port,int backlog) throws IOException 其 ...