【问题标题】:Why does const_cast remove constness for a pointer but not for a pointer to a const?为什么 const_cast 删除指针的 constness 而不是指向 const 的指针?
【发布时间】:2023-04-01 04:05:01
【问题描述】:

我了解 const_cast 可用于指针和引用。

我假设const_cast 的输入应该是一个指针或引用。我想知道如果输入是指向const int 的指针/引用,为什么它不会删除常量?

以下代码按预期工作。

  1. const_cast 带多级指针

    int main()
    {
        using std::cout;
        #define endl '\n'
        const int * ip = new int(123);
        const int * ptr = ip;
        *const_cast<int*>(ptr) = 321;
        cout << "*ip: " << *ip << endl;  // value of *ip is changed to 321
    }
    

    但是当我尝试指向const int 或引用const int 时,值似乎没有改变。

  2. const_cast 参考 const int

    int main()
    {
        using std::cout;
        #define endl '\n'
        const int i = 123;
        const int & ri = i;
        const_cast<int&>(ri) = 321;
        cout << "i: " << i << endl;  // value in 'i' is 123
    }
    
  3. const_cast 带有指向 const int 的指针

    int main()
    {
        using std::cout;
        #define endl '\n'
        const int i = 123;
        const int * ri = &i;
        *const_cast<int*>(ri) = 321;
        cout << "i: " << i << endl;  // value in 'i' is 123
    }
    

(1) 按预期工作,但我无法理解为什么 (2)(3) 不起作用我认为const_cast 的输入是指针/引用。

请帮助我理解这背后的理念。谢谢。

【问题讨论】:

  • 所有 3 个示例都是未定义的行为,来自 CppCoreGuidelines:不要抛弃 const。它用 const 制造谎言。如果变量实际声明为 const,则“抛弃 const”的结果是未定义的行为。
  • 尝试更改 const 对象 (const int) 的值是未定义的行为,这就是 2 和 3 不起作用的原因。 1没有任何const int,只有一个intconst int*指向它,所以没关系。
  • @Kaldrr 示例 1 定义明确。
  • 您对"\n" 字面量有异议吗?你的 sn-ps 中的这个 endl 宏让我感觉很不舒服 :)
  • @lubgr:算了,我没在里面看到#define endl '\n'——那很奇怪……

标签: c++ const-cast


【解决方案1】:

通过const_cast 修改常量是未定义的行为。

编译器看到你正在尝试打印一个常量变量,知道它永远不会改变,所以编译:

cout << "i: " << i << endl;

到:

cout << "i: " << 123 << endl;

请参阅:https://godbolt.org/z/bYb0mx。启用优化后,它会优化您的代码以仅打印 123:https://godbolt.org/z/4Ttlmj

编译器最终会做出假设以创建更快/更小的代码,如果您进入未定义行为的领域,其中一些假设可能不正确并产生令人惊讶的结果。

【讨论】:

    【解决方案2】:

    有两种常量。

    object 的常量是对象的固有属性。它不能改变。

    想想印刷书中的一页。可以看成一串字符,不能更改。它说的是它所说的,就是这样。所以它是const string

    现在想想黑板。上面可能写了一些东西。你可以把它擦掉,然后写点别的。所以黑板是一个非常量的string

    另一种常量是指针常量和引用常量。这种常量性不是指向对象的固有属性,而是权限。它表示您不允许通过此指针修改对象。它没有说明对象本身是否可以修改。

    所以如果你有一个 const 指针,你不一定知道它真正指向什么。也许它是一个书页。也许它是一块黑板。指针不告诉。

    现在,如果您确实知道它确实是一块黑板,您可能会很讨厌并要求允许继续更改上面写的内容。这就是 const_cast 所做的。它允许你做某事。

    如果您要求修改字符串的权限,结果是打印页面,会发生什么?你得到你的许可,你继续擦拭它......并且......究竟发生了什么是undefined。也许什么都没有。也许打印被弄脏了,您既不能识别原始字符串,也不能在上面写任何东西。也许你的世界会爆炸成碎片。你可以试试看,但不能保证明天也会发生同样的事情。

    【讨论】:

    • 好答案;但你可能想提一下这里实际发生的事情。优化器看到const int i = 123; 并说“i 必须始终是123”。这是正确的,因为没有办法 除了 UB 来改变它。然后,当您在其他地方使用i 时,它会说“我可以使用123,为什么还要使用i?听起来更快!”同时,i 有一个内存位置,并且那个内存位置修改了;如果你有一个指向i 的指针,编译器无法证明它指向i,它会在实际读取内存时得到321
    • @Yakk "同时,i 有一个内存位置,并且那个内存位置修改了;" - 不,不一定。由于iint const,它可能位于ROM 中,并且编译器会因为UB 而放弃对它的每次写访问...
    • @martin 是的,它是 UB。我在描述一种常见的情况,而不是保证的情况。 (顺便说一句,在 main 之外,将采用地址的自动存储变量放在 ROM 中可能很困难;如果有任何递归的可能性,则 eaxh 递归需要不同的地址。)
    【解决方案3】:

    (2)和(3)原理相同,这里只讲(2)。

    线

    const_cast<int&>(ri) = 321;
    

    具有未定义的行为。

    您不能根据标准修改const 对象,即使使用const_cast 也不行。如果您从指针/引用中删除const,并修改指向/引用的对象,则首先不能将指向/引用的对象声明为const

    const_cast 应仅在您出于某种原因拥有指向某事物的 const 指针并且您知道某事物未声明为 const 时使用。

    【讨论】:

    • 另一个用例是与不修改对象但常量不正确的 API(通常是早期的 C)互操作。
    猜你喜欢
    • 2011-01-14
    • 2015-09-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-04-14
    • 2012-07-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多