专栏首页C++核心准则原文翻译C++核心准则ES.50:不要执行去掉const属性的转换

C++核心准则ES.50:不要执行去掉const属性的转换

ES.50: Don't cast away const

ES.50:不要执行去掉const属性的转换

Reason(原因)

It makes a lie out of const. If the variable is actually declared const, modifying it results in undefined behavior.

这使代码避开了常量属性的约束。如果变量实际上具有常量属性,修改它会导致无定义的行为。

Example, bad(反面示例)

void f(const int& x)
{
    const_cast<int&>(x) = 42;   // BAD
}

static int i = 0;
static const int j = 0;

f(i); // silent side effect
f(j); // undefined behavior
Example(示例)

Sometimes, you may be tempted to resort to const_cast to avoid code duplication, such as when two accessor functions that differ only in const-ness have similar implementations. For example:

有时,你可能想借助const_cast来避免代码重复,例如两个编写实现相似,只有const属性不同的访问方法。例如:

class Bar;

class Foo {
public:
    // BAD, duplicates logic
    Bar& get_bar() {
        /* complex logic around getting a non-const reference to my_bar */
    }

    const Bar& get_bar() const {
        /* same complex logic around getting a const reference to my_bar */
    }
private:
    Bar my_bar;
};

Instead, prefer to share implementations. Normally, you can just have the non-const function call the const function. However, when there is complex logic this can lead to the following pattern that still resorts to a const_cast:

更好的办法是共享实现方式。通常,你只要让非常量函数调用常量函数就行了。然而,如果逻辑很复杂,这种方式会导致下面的代码模式,还是需要借助const_cast。

class Foo {
public:
    // not great, non-const calls const version but resorts to const_cast
    Bar& get_bar() {
        return const_cast<Bar&>(static_cast<const Foo&>(*this).get_bar());
    }
    const Bar& get_bar() const {
        /* the complex logic around getting a const reference to my_bar */
    }
private:
    Bar my_bar;
};

Although this pattern is safe when applied correctly, because the caller must have had a non-const object to begin with, it's not ideal because the safety is hard to enforce automatically as a checker rule.

这种方式在正确实现的情况下是安全的,但是调用者必须准备一个非常量对象。由于这种安全性很难通过自动执行的规则来保证,不够理想。

Instead, prefer to put the common code in a common helper function -- and make it a template so that it deduces const. This doesn't use any const_cast at all:

更好的方式是将共通代码放到一个共通的辅助函数中--并且将辅助函数设计为模板函数。这种方式不需要const修饰。也就不需要const_cast了。

class Foo {
public:                         // good
          Bar& get_bar()       { return get_bar_impl(*this); }
    const Bar& get_bar() const { return get_bar_impl(*this); }
private:
    Bar my_bar;

    template<class T>           // good, deduces whether T is const or non-const
    static auto get_bar_impl(T& t) -> decltype(t.get_bar())
        { /* the complex logic around getting a possibly-const reference to my_bar */ }
};
Exception(例外)

You may need to cast away const when calling const-incorrect functions. Prefer to wrap such functions in inline const-correct wrappers to encapsulate the cast in one place.

如果调用一个不接受常量的函数,你可能需要通过转换去掉常量属性。最好将这样的函数封装在一个要求常量的行内包装函数内,以便在将这种转换限制在一个地方。

Example(示例)

Sometimes, "cast away const" is to allow the updating of some transient information of an otherwise immutable object. Examples are caching, memoization, and precomputation. Such examples are often handled as well or better using mutable or an indirection than with a const_cast.

有时,去掉常量属性的转换用于临时允许更新信息,其他情况下都是不可改变的对象。这样的例子包括缓存,备忘录和预处理等。对于这类情况,使用mutable或者间接操作也可以,甚至比使用const_cast更好。

Consider keeping previously computed results around for a costly operation:

考虑为了高成本计算而长期保存计算结果的情况:

int compute(int x); // compute a value for x; assume this to be costly

class Cache {   // some type implementing a cache for an int->int operation
public:
    pair<bool, int> find(int x) const;   // is there a value for x?
    void set(int x, int v);             // make y the value for x
    // ...
private:
    // ...
};

class X {
public:
    int get_val(int x)
    {
        auto p = cache.find(x);
        if (p.first) return p.second;
        int val = compute(x);
        cache.set(x, val); // insert value for x
        return val;
    }
    // ...
private:
    Cache cache;
};

Here, get_val() is logically constant, so we would like to make it a const member. To do this we still need to mutate cache, so people sometimes resort to a const_cast:

这里,get_val()逻辑上应该是常量函数,因此我们希望将它设计成常量成员。为了做到这一点,我们还是需要修改缓存的内容,因此人们有时会借助const_cast:

class X {   // Suspicious solution based on casting
public:
    int get_val(int x) const
    {
        auto p = cache.find(x);
        if (p.first) return p.second;
        int val = compute(x);
        const_cast<Cache&>(cache).set(x, val);   // ugly
        return val;
    }
    // ...
private:
    Cache cache;
};

Fortunately, there is a better solution: State that cache is mutable even for a const object:

幸运的是,存在一个更好的解决方案:将cache声明为mutable(即使对于const对象也有效):

class X {   // better solution
public:
    int get_val(int x) const
    {
        auto p = cache.find(x);
        if (p.first) return p.second;
        int val = compute(x);
        cache.set(x, val);
        return val;
    }
    // ...
private:
    mutable Cache cache;
};

An alternative solution would be to store a pointer to the cache:

另外一个解决方案是将cache保存为指针。

class X {   // OK, but slightly messier solution
public:
    int get_val(int x) const
    {
        auto p = cache->find(x);
        if (p.first) return p.second;
        int val = compute(x);
        cache->set(x, val);
        return val;
    }
    // ...
private:
    unique_ptr<Cache> cache;
};

That solution is the most flexible, but requires explicit construction and destruction of *cache (most likely in the constructor and destructor of X).

这个方案更有弹性,但是需要cache对象的显式构造和析构(最有可能位置是X的构造函数和析构函数)

In any variant, we must guard against data races on the cache in multi-threaded code, possibly using a std::mutex.

对于所有变量,我们必须防止多线程编程时在cache上发生数据竞争,或许可以使用std::mutex。

Enforcement(实施建议)
  • Flag const_casts.
  • 标记const_cast.
  • This rule is part of the type-safety profile for the related Profile.
  • 这条准则是类型安全群组及相关群组的一部分。

原文链接

https://github.com/isocpp/CppCoreGuidelines/blob/master/CppCoreGuidelines.md#es50-dont-cast-away-const

本文分享自微信公众号 - 面向对象思考(OOThinkingDalian),作者:面向对象思考

原文出处及转载信息见文内详细说明,如有侵权,请联系 yunjia_community@tencent.com 删除。

原始发表时间:2020-05-17

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • C++核心准则编译边学-F.20 输出结果时更应该使用返回值而不是输出参数

    A return value is self-documenting, whereas a & could be either in-out or out-on...

    面向对象思考
  • C++核心准则​Con.3:默认情况下,传递参照常量的指针或引用

    To avoid a called function unexpectedly changing the value. It's far easier to r...

    面向对象思考
  • C++核心准则​Con.1:默认情况下使对象不可修改

    Immutable objects are easier to reason about, so make objects non-const only whe...

    面向对象思考
  • Effective C++条款3 我可以不使用const?

    const是C++中用得非常频繁的一个关键字,但是如果你不使用这个关键字,对程序的运行结果影响不大,那么,我可以不使用const吗?

    ACM算法日常
  • 初级程序员面试不靠谱指南(二)

    3.read-only的const。如果你突然冒出一句看似很高深的话但又不解释一般都是装逼,就像前面提到过const准确的应该理解为一个read-only的变量...

    一心一怿
  • 多才多艺的const

    这样的global实际上是一个常量,这是C++用来取代宏定义的其中一种措施,const常量有类型检测,提高编译器的效率。

    用户2617681
  • const特性总结(不断更新)

    作者:bakari  时间:2012.6.5 1、指向const对象的指针---const int *cptr; i、在此,cptr是指向int类型的const...

    CloudDeveloper
  • const的那些事

    在我们使用c/c++的时候,或者在面试的时候,会被问道与const相关的问题,比如const修饰放在指针的哪个位置,const修饰的引用传递与值传递等等,这些究...

    公众号guangcity
  • C++雾中风景3:const用法的小结

    const关键字,翻译成中文是常量,常数的意思。所以在绝大多数场合之中,const是来定义常量的,定义常量也是好的编程习惯。在C类语言之中,定义常量通常会使用宏...

    HappenLee
  • C++primer学习笔记(一)

    震八方紫面昆仑侠

扫码关注云+社区

领取腾讯云代金券