正确使用C++的const_cast
C++的四种类型转换之const_cast
前言
引用《Effective C++ 中文版第三版》中条款27 "尽量少做转型动作"中的一段话:
C++规则的设计目标之一是,保证“类型错误”绝对不可能发生,理论上如果你的程序很“干净”的通过编译,就表示它并不企图在任何对象上执行任何不安全、无意义、愚蠢荒谬的操作。这是一个极具价值的保证,可别草率的放弃它。
不幸的是,转型(cast)破坏了类型系统(type system),那可能导致任何种类的麻烦,有些容易辨识,有些非常隐晦。
现实情况是类型转换
在开发中无法避免,在开始探讨C++的类型转换以前我们先看下C风格
的类型转换。
(T)varibale
T(variable)
这种旧式类型转换
目前依旧经常被用到,但是新式类型转换
却更应该被我们去使用,这是因为:
- 1.容易被辨识,从而更容易在程序出问题时找到问题所在
- 2.职责划分更明细,编译器更可能在编译期找到问题。
- 3.有更强的类型转换能力。
目前C++提供的四种新式类型转换为
const_cast<T>()
dynamic_cast<T>()
reinterpret_cast<T>()
static_cast<T>()
下面我们就const_cast
,分别研究下其用途
以及适用的使用场景
。
const_cast
const_cast<new type>(express)
的主要用途是,移除
对象的常量
性,并且也是唯一具有此能力的C++风格的转型操作符
在C++11中,const_cast可以完成以下类型转换
- 两个可能指向同一类型的多级指针可以相互转换,而不考虑每一层上的cv性质(const and volatile)。
- 空指针值可以转换为新类型的空指针值.
可以看出,const_cast主要针对指针进行操作,其能力可以大概被总结为:
1、常量指针被转化成非常量的指针,并且仍然指向原来的对象; 2、常量引用被转换成非常量的引用,并且仍然指向原来的对象;
const_cast的返回结果根据需要转换出的新类型,可以区别为:
- 如果new type是lvalue引用,或者是函数类型的rvalue引用,则返回lvalue。
- 如果new type是对象类型的rvalue引用,则返回xvalue。
- 否则返回prvalue。
以下时关于几个value的解释:
- lvalue (Left-hand-side value)
- rvalue (Right-hand-side value)
- xvalue (eXpiring value)
- prvalue (Pure rvalue)
- glvalue (Generalized lvalue)
For all the values, there were only two independent properties:
- "has identity" – i.e. an address, a pointer, the user can determine whether two copies are identical, etc.use "i" represent
- "can be moved from" – i.e. we are allowed to leave to source of a "copy" in some indeterminate, but valid state.use "m" represent.
There are four possible composition:
- iM: has identity and cannot be moved from (defined as lvalue)
- im: has identity and can be moved from (defined as xvalue)
- Im: does not have identity and can be moved from (defined as prvalue)
- IM: doesn't have identity and cannot be moved (他认为这种情况在 C++ 中是没有用的)
以上出自于https://cloud.tencent.com/developer/article/1493839
需要注意的是,const_cast中:
- 指向
函数
的指针和指向成员函数
的指针不受const_cast约束。 - const_cast可以形成一个指向非const类型的引用或指针,它实际上引用了一个const对象,也可以形成一个指向非volatile类型的引用或指针,它实际上引用了一个volatile对象。
- 通过非const访问
路径
修改const对象并通过非volatile glvalue引用volatile对象会导致未定义
的行为。
未定义行为
:C++标准对此类行为没有做出明确规定.同一份代码在使用不同的编译器会有不同的效果
**《C++ Primer》
和《Effective C++》
是C++开发者必不可少的书籍,如果你想入门C++,以及想要精进C++开发技术,这两本书可以说必须要有。此外,《Linux高性能服务器编程》
以及《Linux多线程服务端编程:使用muduo C++网络库》.(陈硕)》
是快速提高你的linux开发能力的秘籍。在网上搜索相关资源也要花费一些力气,需要的同学可以关注公众号【程序员DeRozan】
,回复【1207】
**快速免费领取~
下面通过例子来分析const_cast的用法,C++版本为C++11
1.new type为左值引用
将一个常量左值引用转换为一个非常量左值引用:
int i = 3; // i is not declared const
const int &rci = i;
const_cast<int &>(rci) = 4; // OK: modifies i
std::cout << "i = " << i << '\n';
控制台输出
sh-4.4$ ./build/linux/x86_64/release/Class-convert
i = 4
即,如果new type为左值引用
,则返回值是一个左值
。
2. new type为函数类型的右值引用
如果new type的类型为函数右值引用
,则const_cast的返回值为左值
。
void printHello(){
cout<<"hello world"<<endl;
}
void printHello2(){
cout<<"hello world 2"<<endl;
}
int main(){
const std::reference_wrapper<void()>x = std::ref(printHello);
// x = printHello2; //编译错误,const类型的函数右值引用
const_cast<std::reference_wrapper<void()> &&>(x) = printHello2;
x();
}
3.new type为对象类型的右值引用
将一个常量对象右值引用
,转换为一个非常量
的对象类型引用:
class OBJ {
public:
int num;
OBJ(int i){num = i;}
~OBJ(){}
};
int main()
{
const OBJ&& obj = OBJ(1);
cout<<"obj.num="<<obj.num<<endl;
// obj.num = 3; //编译错误,这里是const引用
// const_cast<OBJ&&>(obj).num = 4;// 编译错误,error: using xvalue (rvalue reference) as lvalue
// cout << "obj.num=" << obj.num<<endl;
OBJ &&obj2 = const_cast<OBJ &&>(obj); //编译通过
obj2.num = 3;
cout << "obj.num=" << obj.num << endl;
}
4.指向函数的指针和指向成员函数的指针不受约束
struct type
{
int i;
type() : i(3) {}
void f(int v) const
{
// this->i = v; // compile error: this is a pointer to const
const_cast<type *>(this)->i = v; // OK as long as the type object isn't const
}
};
int main(){
[[maybe_unused]] void (type::*pmf)(int) const = &type::f; // pointer to member function
// const_cast<void(type::*)(int)>(pmf); // compile error: const_cast does
// not work on function pointers
}
5.通过非const访问路径修改const对象导致未定义行为
const int j = 3; // j is declared const
[[maybe_unused]] int *pj = const_cast<int *>(&j);
// *pj = 4; // undefined behavior
6.const_cast只能用来修改指针,引用
const int j = 3; // j is declared const
int ppj = const_cast<int>(j); // 编译错误,invalid use of const_cast with type ‘int’, which is not a pointer, reference, nor a pointer-to-data-member type
from:https://zhuanlan.zhihu.com/p/611350793