正确使用const_cast <>

ere*_*eOn 38 c++ const-cast

作为一个通用规则,const_cast<>()在C++代码中使用它通常被认为是一种不好的做法,因为它(大多数情况下)揭示了设计中的一个缺陷.

虽然我完全同意这种说法,但是我不知道什么是例使用const_cast<>()确定唯一的解决办法.

你能不能给我一些你认识/遇到过的例子?

非常感谢你.

jk.*_*jk. 26

它几乎被设计为仅用于不是const正确的旧API,即使用一个无法更改的函数,它具有非const接口,但实际上并没有改变接口上的任何内容


GMa*_*ckG 18

像其他人所说的那样,它的主要目的是const从对象中删除以传递给非常规的正确函数,你知道这些函数不会修改参数.

有一个技巧(由Meyers?)来避免代码重复,它是这样的:

struct foo
{
    const return_type& get(void) const
    {
        // fancy pants code that you definitely
        // don't want to repeat

        return theValue; // and got it
    }

    return_type& get(void)
    {
        // well-defined: Add const to *this,
        // call the const version, then
        // const-cast to remove const (because
        // *this is non-const, this is ok)
        return const_cast<return_type&>(static_cast<const foo&>(*this).get());
    }
};
Run Code Online (Sandbox Code Playgroud)

  • 我认为这个技巧几乎总是比复制`get()`中`get()const`的内容更糟糕.在这比复制和粘贴更好的情况下,模板助手(对于const实例化一次,对于非const实例一次)会更好,因为它将允许编译器验证花式裤子代码确实返回可修改的东西在'this`可修改的情况下.它可能不会,它有时会返回对`const`全局变量的引用,所以这个技巧会强制你手动验证花式裤子代码的const正确性.坏. (7认同)
  • 如果“花哨的裤子”代码是 0 行,而 `get` 只返回一个数据成员怎么办?那么你会避免复制粘贴吗?我的主张是这个技巧没有好的案例。要么代码足够简单,可以复制粘贴,要么太复杂而无法手动分析常量正确性。没有太多可以复制的中间立场,但又少到你不想要自动化的常量正确性。在 Meyer 的示例 IIRC 中,花哨的裤子代码是一个边界检查:所以将检查放在一个辅助函数中,与我们通常共享代码的方式相同;-) (2认同)
  • @n1ck:这些函数将是相邻的,但肯定存在这样的风险,例如有人会进入并为其中一个添加边界检查但忘记了另一个。如果我担心我会让它们各自调用一个通用的模板辅助函数,从而消除*两者*的风险。这不是一个非此即彼的情况。最终,这个技巧根据另一件事来实现一件事,而它在逻辑上*不能*一般地根据另一件事来实现。从非常量版本到 const 的调用仅由于 const 的实现细节才有效(它从不返回 const 对象)。这就是为什么我不喜欢它。 (2认同)
  • @ n1ck:好的.模板帮助器看起来像`template <typename SELF,typename RET> RET&get_helper(SELF&self){/*fancy pants code*/return self.theValue; }`.然后将其称为`get_helper <const foo,const return_type>(*this)`来自const`get`和`get_helper <foo,return_type>(*this)`来自非const. (2认同)
  • @n1ck:“我觉得这近乎侮辱”——你如何在互联网上生活?我试图传达 (a) 您对两个模板参数进行常量限定的事实,以及 (b) 从 GMan 的首选代码到我的代码有一个简单的转换,这并不真正依赖于 getter 的内容。我(真诚地)为此道歉,因为在这样做时我必须演示关键字“this”的使用,您无疑也已经熟悉了;-)。无论如何,这只是一种简单化的方法,在 GMan 的示例中,您可以使用 C++0x `decltype` 轻松删除模板参数之一。 (2认同)

Ale*_* C. 17

const_cast也被用来删除volatile修饰语,正如在这篇(有争议的)文章中付诸实践:

http://www.drdobbs.com/184403766


Tim*_*sch 10

我同意你的陈述,它的正常使用是因为你需要隐藏"设计缺陷".

IME的典型使用场景之一是当您尝试将C++与现有C代码接口时.许多现有的C代码都采用C字符串,char *即使字符串未被修改,而它们通常表示为const char *在C++ 中转换为a的字符串.这是通常使用const_cast解决的两种语言之间的阻抗不匹配.当然,你最好是非常确定你与接口的代码没有得到关于修改在传递数据的任何可爱的想法.

我会说这是新编写的代码中的代码,但是为了与旧的C和C++代码接口,这是一个必要的恶魔.也就是说,我会非常警惕const_cast任何非POD对象所需的代码,因为这通常是一个应该在设计级而不是代码级解决的问题.


Kan*_*sto 6

(在我看来)一种合法用途是与std::set迭代器一起使用。它们始终为const,以防止更改集合中使用的密钥。更改密钥会破坏集合的内部结构,并导致未定义的行为。

但是,只要密钥不变,就可以安全地更改对象中的其他数据。

假设您有一个std::set类似这样的人:

std::set<MyObject> some_set;
Run Code Online (Sandbox Code Playgroud)

像这样的课:

class MyObject {
    public:
        MyObject(const std::string &key)
            : key_(key) {}

        bool operator<(const MyObject &other) const {
            return key_ < other.key_;
        }

    private:
        // ...
        // <some other data>
        // ...

        const std::string key_;
};
Run Code Online (Sandbox Code Playgroud)

在上面的示例中,键已经是const,因此,即使您修改对象,也无法破坏集合的内部结构。

通常,您只能从const集合迭代器中获取引用:

const MyObject &object = *some_set_iterator;
Run Code Online (Sandbox Code Playgroud)

但是由于键是const,所以const_cast对于取消引用的迭代器是安全的:

MyObject &object = const_cast<MyObject &>(*some_set_iterator);
Run Code Online (Sandbox Code Playgroud)