C++11 利用const_cast和type_traits修改类成员常量的通用模板函数
2015-11-21 11:38
429 查看
对于
如果你的代码中有多处用到修改不同类型常量的地方,你就得写很多跟上面相似的3行代码,好烦,能不能简化一下呢?
下面的代码利用C++11中的type_trait(类型萃取)将代码简化为两行,其基本原理是通过指向常量的引用来修改常量的内容
在第一行代码中先用
然后用std::remove_const移除获取的类型的const修饰符,变成
然后基于上一步的结果再使用std::add_lvalue_reference给类型添加左值引用,结果是
然后再调用
这里使用了
上面这个复杂的写法主要是为了实现类型无关性,可以不关心
简单的写法是这样的:
通过上面的改进确实把代码简化成了2行。
我们可以把上面的代码写成一个通用的模板函数。。。。以后只要调用模板函数就成了,就可以把代码简化为1行。
不论
类成员常量修改测试 class_A.c=5
局部基本类型常量修改测试 c=21
局部指针常量修改测试 *p_c=5
局部unique_ptr类常量修改测试 *u1.get()=5
从上面的输出第二行可以看出,
const定义的常量,不能直接修改它的值,这是这个限定符最直接的表现。但是某种情况下我们真的需要突破
const限定修改其常量,C++11中可以使用
const_cast转换符是用来移除变量的
const限定符。关于
const_cast的用法网上可以找到很多很多,基本的原理就是通过指向常量的指针来修改常量的内容,就像下面这样:
const int c = 21; //下面三行代码实现修改常量c const int* c_p = &c; //1.定义一个常量指针 int* m = const_cast<int*>(c_p);//2.将常量指针用const_cast转为一个新的非常量指针 *m = 7;//3.通过指向常量的非常量指针修改常量内容
如果你的代码中有多处用到修改不同类型常量的地方,你就得写很多跟上面相似的3行代码,好烦,能不能简化一下呢?
下面的代码利用C++11中的type_trait(类型萃取)将代码简化为两行,其基本原理是通过指向常量的引用来修改常量的内容
const int c = 21; //下面两行代码实现修改常量c auto &r_c =const_cast<typename std::add_lvalue_reference<typename std::remove_const<decltype(c)>::type>::type>(c);//1.定义一个指向常量c的非常量引用 r_c=5;//2.通过指向常量的引用来修改常量的内容
在第一行代码中先用
decltype获取c的类型,结果是
const int,
然后用std::remove_const移除获取的类型的const修饰符,变成
int,
然后基于上一步的结果再使用std::add_lvalue_reference给类型添加左值引用,结果是
int&
然后再调用
const_cast,就是
const_cast<int&>(c);
这里使用了
auto关键推导
r_c的类型。这里
r_c的类型就是
int&,指向常量
c的非常量引用。
上面这个复杂的写法主要是为了实现类型无关性,可以不关心
c的数据类型。
简单的写法是这样的:
const int c = 21; //下面两行代码实现修改常量c auto &r_c =const_cast<int&>(c);//1.定义一个指向常量c的非常量引用 r_c=5;//2.通过指向常量的引用来修改常量的内容
通过上面的改进确实把代码简化成了2行。
我们可以把上面的代码写成一个通用的模板函数。。。。以后只要调用模板函数就成了,就可以把代码简化为1行。
#include <type_traits> /* 修改常量 */ template <typename T> void inline modify_const(const T& const_var,const T &)noexcept{ auto &ref_var =const_cast<T&>(const_var); //将两个参数都转为非常量引用 auto &ref_new =const_cast<T&>(new_value); ref_var=std::move(ref_new);// 转为右值,以适合比如unique_ptr这种不提供复制操作符的对象 } //在 gcc5和vs2015下编译通过
不论
new_value是个左值还是右值都可以正常调用
modify_const,模板函数
modify_const的用法:
const size_t c = 21; modify_const(c,5ULL);//调用模板函数将常量c的值修改为5, //注意size_t 在64位系统下定义为unsigned long long,所以这里的参数5必须有类型限定后缀ULL才能与第一个参数的基本类型保持一致,否则编译也不会通过
size_t nv=5; size_t cv=200; size_t *const p_c=&nv; modify_const(p_c,&cv);//修改指针常量
const unique_ptr<int> u1(new int(311)); const unique_ptr<int> u2(new int(511)); modify_const(u1,u2);//修改对象常量
modify_const只是在C++语法上实现了修改
const修饰的常量,其实只对类成员常量以及非基本类型的局部常量有效,对于函数局部基本类型常量修改是无效的。下面是完整的验证代码:
//============================================================================ // Name : TestConst.cpp // Author : // Version : // Copyright : Your copyright notice // Description : Hello World in C++, Ansi-style //============================================================================ #include <iostream> #include <memory> #include <type_traits> using namespace std; /* 修改常量 */ template <typename T> void inline modify_const(const T& const_var,const T & new_value)noexcept{ auto &ref_var =const_cast<T&>(const_var); //将两个参数都转为非常量引用 auto &ref_new =const_cast<T&>(new_value); ref_var=std::move(ref_new);// 转为右值,以适合比如unique_ptr这种不提供复制操作符的对象 } struct class_A{ const int c=21; }; void test_const(){ const int c=21; auto &r_c=const_cast<int&>(c); r_c=5; cout<<"局部基本类型常量修改测试 c="<<c<<endl; } int main() { class_A a; auto &r_c=const_cast<int&>(a.c); r_c=5; cout<<"类成员常量修改测试 class_A.c="<<a.c<<endl; test_const(); size_t nv=21; size_t cv=5; size_t *const p_c=&nv; modify_const(p_c,&cv);//修改指针常量 cout<<"局部指针常量修改测试 *p_c="<<*p_c<<endl; const unique_ptr<int> u1(new int(21)); const unique_ptr<int> u2(new int(5)); modify_const(u1,u2);//修改对象常量 cout<<"局部unique_ptr类常量修改测试 *u1.get()="<<*u1.get()<<endl; }
类成员常量修改测试 class_A.c=5
局部基本类型常量修改测试 c=21
局部指针常量修改测试 *p_c=5
局部unique_ptr类常量修改测试 *u1.get()=5
从上面的输出第二行可以看出,
int型的局部常量没有被真正修改。
注意!!!
对于全局常量或类的静态常量成员,因为位于程序的常量存储区,受CPU指令级的内存保护(只读),所以是不能被修改的,虽然修改全局常量或类成员静态常量的代码也能编译通过,但实际运行时会抛出内存访问冲突的异常。参见 《mprotect: 设置内存访问权限》相关文章推荐
- 在 Qt4 中使用 C++11
- centos安装devtoolset-3支持gcc 4.9.2
- 使用eclipse编译含有C++11特性的代码
- 怎样在Linux环境编译支持C11
- eclipse支持c++11
- C++11可变参数函数与for循环
- vs2013 编译c++是发现惊天bug
- 简单性能测试函数模板
- 关于C++现状的一些思考
- 用C++11优化矩阵运算的空间和时间效率
- 浅析构造函数之默认构造函数
- c++中返回数组的函数
- C++函数重载的几个问题
- C++11 note-2 字符串 容器 迭代器
- C++11 现代C++风格的新元素
- c++11 lambda表达式浅谈
- C++11之“move”语意
- C++11之Lambda表达式
- C++11特性乱弹
- 掀起C++ 11的神秘面纱(1)