如果从不通过它们修改对实际 const 对象的引用,是否允许 const-casting 掉其常量性?

2021-12-31 00:00:00 language-lawyer constants interface c++

我有一个抽象类,它声明了常量和非常量成员函数.为了便于讨论,我们假设它看起来像这样:

I have an abstract class that declares const and non-const member functions. For the sake of discussion let's say it looks like this:

class record_interface
{
public:
   virtual ~record_interface() = default;

   virtual void set_foo(BoundedFloat) = 0;
   virtual BoundedFloat get_foo() const = 0;
};

这用作记录的高级表示,当保存到光盘并通过线路传输时,该记录具有不同的表示.因此,大多数实现只需要将其成员转换为所需的高级表示.

This is used as a high-level representation of a record that has different representations when saved to disc and transferred via the wire. So most implementations just need to convert their members to the required high-level representation.

作为有效实现的示例,让我们定义 stored_record.这用于以有损格式存储高级记录:

As an example of a valid implementation let's define stored_record. This is used to store the high-level record in a lossy format:

struct stored_record
{
    int16_t foo;
};

stored_record 可以实现 record_interface 是有道理的,但由于各种原因它不能(例如,它需要是 trivially_copyable).我们可以制作一个包装器来实现它的接口:

It makes sense that stored_record can implement record_interface but for various reasons it can't (eg. it needs to be trivially_copyable). We can make a wrapper that implements the interface for it:

class record_wrapper : public record_interface
{
public:
  record_wrapper(stored_record & wrapped)
    : wrapped_(wrapped) {}

  void set_foo(BoundedFloat value) final { wrapped_.foo = convert_to_int16(value); }
  BoundedFloat get_foo() const final { return convert_from_int16(wrapped_.foo); }

private:
  stored_record & wrapped_;
};

现在的问题是,当给定 const stored_record & 时,我们不能使用包装器,因为包装器存储一个可变引用.我们也不能让它存储非常量引用,因为它无法实现非常量 setter 函数.

Now the problem is that we can't use the wrapper when given a const stored_record & since the wrapper stores a mutable reference. We also can't make it store a non-const reference as it won't be able to implement the non-const setter function.

现在我想知道提供一个 const_cast 远离的工厂函数是否有效一个 const stored_record &const 但也返回一个 const 包装器 以便引用实际上不能被修改:

Now I was wondering if it would be valid to provide a factory function that const_casts away a const stored_record & 's const but also returns a const wrapper so that the reference cannot actually be modified:

record_wrapper make_wrapper(stored_record & wrapped) {return {wrapped}; }
record_wrapper const make_wrapper(stored_record const & wrapped) { return {const_cast<stored_record &>(wrapped)}; }

EDIT:返回一个 const record_wrapper 不会真正将返回值限制为 const,一个解决方案可以是返回一个 const_wrapper 或类似的东西.

EDIT: returning a const record_wrapper will not really restrict the returned value to be const, a solution can be to return a const_wrapper<record_wrapper> or something similar.

这是 const_cast 的有效用法还是由于 const_cast 去掉了对一个引用的 const-ness 而导致的未定义行为实际上是 const 对象 - 即使它从未被修改过.

Is this a valid usage of const_cast or is it undefined behaviour due to const_casting away the const-ness of a reference to an actually const object - even though it is never modified through it.

推荐答案

Per https://en.cppreference.com/w/cpp/language/const_cast:

const_cast 可以形成一个引用或指向实际引用 const 对象 或指向实际引用 易失性对象.通过非常量访问路径修改常量对象并通过非易失性引用易失性对象 glvalue 导致未定义的行为.

const_cast makes it possible to form a reference or pointer to non-const type that is actually referring to a const object or a reference or pointer to non-volatile type that is actually referring to a volatile object. Modifying a const object through a non-const access path and referring to a volatile object through a non-volatile glvalue results in undefined behavior.

因此,const_cast 本身是允许的(并且定义良好),即使通过生成的非常量引用实际修改对象是未定义的行为.

So, the const_cast itself is allowed (and well-defined), even though it would be undefined behavior to actually modify the object via the resulting non-const reference.

相关文章