正确使用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++ 中是没有用的)

以上出自于

需要注意的是,const_cast中:

  1. 指向函数的指针和指向成员函数的指针不受const_cast约束。
  2. const_cast可以形成一个指向非const类型的引用或指针,它实际上引用了一个const对象,也可以形成一个指向非volatile类型的引用或指针,它实际上引用了一个volatile对象。
  3. 通过非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

 

posted @ 2023-06-14 22:24  imxiangzi  阅读(170)  评论(0编辑  收藏  举报