C++11 利用const_cast和type_traits修改类成员常量的通用模板函数

原创 2015年11月21日 11:38:32

对于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: 设置内存访问权限》

版权声明:本文为博主原创文章,转载请注明源地址。 举报

相关文章推荐

c++11 之type_traits

1.type_traits-类型萃取  (1)type_traits可以在一定程度上消除 switch-case 或者 if-else语句,降低程序的复杂度 (2)可以在编译期就检查出是否是正确类型 ...

【C++11学习笔记】类型判断的type_traits学习

一、简单的type_traits我理解的type_traits是利用C++模板特性和static、enum特性定义编译器常量,例如//std::integral_constant源码 typelate...
  • yockie
  • yockie
  • 2016-08-23 02:03
  • 2456

精选:深入理解 Docker 内部原理及网络配置

网络绝对是任何系统的核心,对于容器而言也是如此。Docker 作为目前最火的轻量级容器技术,有很多令人称道的功能,如 Docker 的镜像管理。然而,Docker的网络一直以来都比较薄弱,所以我们有必要深入了解Docker的网络知识,以满足更高的网络需求。

模版元编程:C++11中type traits的部分实现

C++11新加入的type_traits头文件提供了模版元编程中常用的type trait基础设施,这些type traits基于编译期间的运算,能够极早提示出程序中的错误。这些type traits...

C++11模板:如何判断类中是否有指定名称的成员变量?

如何判断类中有指定的成员函数,网上可以找到不少的文章,比如下面这两篇就写得很详细了 《C++11之美》 《C++模板,判断是否存在成员函数,实现差异化操作 》 我现在关心的是如何判断一个类中...
  • 10km
  • 10km
  • 2016-04-10 17:11
  • 1681

C++11特有的数值、数组初始化方法、常量的符号名称 const和浮点数、bool、自动推断类型auto

符号名称指出了常量表示的内容。 如果程序在多个地方使用同一个常量,则需要修改该常量的值时,只需修改一个符号定义即可。  处理符号常量的方法: 预处理器方法:#define Months 12 C++有...

利用C++11的function和bind功能,实现QStandardItemModel的通用遍历函数

在使用Qt的树形视图和表格视图QTableView和QTreeView时,经常需要遍历所有条目,每种功能都写一个遍历函数既麻烦又不符合编程最简原则,因此,写一个通用的遍历函数是很必要的(类似于std:...

C++11特性--新的类功能--特殊的成员函数(移动构造函数,移动赋值运算符),默认方法和禁用方法(default,delete),委托构造函数,管理虚方法(override,final)

1.新的类功能   (1)特殊的成员函数        *在原有4个特殊成员函数(默认构造函数,复制构造函数,复制赋值运算符和析构函数)的基础上,C++11新增了两个:移动构造函数,移动赋值运算符。这...

c++11调用成员函数mem_fn和适合普通函数指针

在C++11之前,调用一个成员函数指针做为容器的回调算法时,

基于C++11模板元编程实现Scheme中的list及相关函数式编程接口

本文将介绍如何使用C++11模板元编程实现Scheme中的list及相关函数式编程接口,如list,cons,car,cdr,length,is_empty,reverse,append,map,tr...
返回顶部
收藏助手
不良信息举报
您举报文章:深度学习:神经网络中的前向传播和反向传播算法推导
举报原因:
原因补充:

(最多只允许输入30个字)