前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >C++核心准则ES.50:不要执行去掉const属性的转换

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

作者头像
面向对象思考
发布2020-05-25 16:31:07
1.4K0
发布2020-05-25 16:31:07
举报

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(反面示例)

代码语言:javascript
复制
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属性不同的访问方法。例如:

代码语言:javascript
复制
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。

代码语言:javascript
复制
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了。

代码语言:javascript
复制
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:

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

代码语言:javascript
复制
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:

代码语言:javascript
复制
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对象也有效):

代码语言:javascript
复制
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保存为指针。

代码语言:javascript
复制
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

本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-05-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 面向对象思考 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • ES.50: Don't cast away const
  • Reason(原因)
    • Example(示例)
      • Exception(例外)
        • Enforcement(实施建议)
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档