如果从未通过它们进行修改,是否允许对实际 const 对象的引用进行 const 转换?

2022-01-23 00:00:00 language-lawyer constants interface c++

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

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 & 时,我们不能使用包装器,因为包装器存储一个可变引用.我们也不能让它存储一个非常量引用,因为它无法实现非常量设置器函数.

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 wrapper 以便实际无法修改引用:

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 对象――即使它从未被它修改过.

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.

推荐答案

根据 https://en.cppreference.com/w/cpp/language/const_cast:

const_cast 可以形成一个指向非 const 类型的引用或指针,它实际上是在引用 const object 或指向非易失类型的引用或指针,实际上是在引用 易失性对象.通过非 const 访问路径修改 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.

相关文章