constants - D 中的逻辑常量

标签 constants d mutable const-correctness

D 有两种类型的常量:不可变变量是被声明为不可变的变量,并且总是不可变的,而常量变量只是对象的只读版本。

逻辑常量是指一个函数被标记为常量,但允许对一个或多个成员变量进行写访问。它的典型用途是用于惰性求值,例如(在 C++ 中)

struct Matrix
{
  double determinant() const
  {
    if ( m_dirty )
    {
      m_determinant = /* expensive calculation */;
      m_dirty = false;
    }
    return m_determinant;
  }

  void set(int i, int j, double x) { m_dirty = true; ...; }

  mutable bool m_dirty;
  mutable double m_determinant;
};

在这里,determinant()const ,但仍然可以修改 m_dirtym_determinant由于它们被标记为 mutable .

D const(FAQ)说 D2 不支持逻辑常量,因为它提供的保证很弱,这阻碍了编写并发程序,并使某些优化更加困难。

我完全理解这种担忧,但是如果我们需要逻辑常量怎么办?

考虑上述 Matrix 的情况类,但没有缓存(以及对逻辑常量的任何需要)。还可以想象这个类在我的代码库中使用,并且主要通过常量引用访问。

现在考虑分析显示 determinant()函数是代码中的瓶颈,而且它通常被重复访问,其值很少改变,即缓存,如上所述,将是一个完美的优化。

如果没有逻辑常量,我怎么能做到这一点?遍历我的代码库,将常量引用更改为非常量引用不是一个选项(出于显而易见的原因)。

我有哪些选择(如果有)?

最佳答案

我认为发布recent thread on this topic on the D newsgroup的基本结论是合适的。在这里,以便那些没有跟踪该列表的人仍然可以获得适当的答案。

D 的 const 不是逻辑常量。它是可传递的并且是完全常量。该语言在技术上不支持逻辑常量。该语言没有定义任何改变 const 对象的方法。

实际上,C++ 也没有逻辑常量。使用 mutable抛弃 const-ness 允许你完全规避 const,这样,从技术上讲,除了你没有在 const 变量上调用任何非常量函数之外,const 实际上并不能保证任何事情。 const 函数实际上是 const 并且不会与您的变量混淆的事实完全按照惯例结合在一起。现在,大多数程序员不会四处抛弃左右常量并使一切都可变,因此在实践中,它非常有用,但它不仅可以完全规避,而且语言还专门为您提供了定义的方法.在 C++ 中 mutable并且抛弃 const 是由语言定义好的和支持的。

D 不会那样做。 D 的 const 实际上是 const。在变量上抛弃 const 然后改变它是未定义的。没有可变的。 D 的 const 有真正的保证(只要你不做任何未定义的事情,比如抛弃某些东西的 const 然后改变它)。这很重要,不仅因为编译器对 D 的保证比对 C++ 的保证强得多,而且因为不可变变量不能以任何方式改变形状或形式。它们可能在只读内存中,如果你试图抛弃不变性并改变这样的变量(段错误可能是可能发生的最好的事情),谁知道会发生什么可怕的事情。并且由于 const 变量实际上可以引用不可变数据,因此至少可以说,抛弃 const 来更改变量或允许以某种方式更改 const 变量是不好的。所以,语言不允许。

现在,作为 BCS points out , D 是一种实用语言。您可以抛弃 const,此时您可以更改变量。因此,例如,您可以使用一个变量来缓存 const 函数的返回值(如果对象的状态发生更改,则该缓存可能会失效)并丢弃 const 来更改它。只要有问题的变量实际上不是不可变的,它就会起作用。但是,这是未定义的行为。一旦你做到了,你就靠自己了。您绕过了类型系统和编译器的保证。您是负责确保您不会在不可变对象(immutable对象)上执行此操作或以其他方式破坏编译器通常保证的内容的人。所以,如果你需要这样做,你可以,但你正在踏入狂野的西部,由你来确保你没有改变你不应该改变的东西。

鉴于只要变量实际上不引用不可变数据,抛弃 const 就可以工作,因此可以创建一个 Mutable模板基本上得到什么mutable在 C++ 中为您提供(因此,它会为您抛弃 const-ness)。 he_the_great gives an example在他的回答中使用了这样的模板。但是使用这样的模板仍然是未定义的行为。在实际不可变的对象上使用它会导致问题。您,程序员,必须确保正确使用它。

因此,D 通过抛弃 const 在技术上使逻辑 const 成为可能,但是为了做到这一点,您必须通过绕过类型系统来超越编译器所保证的范围,并且您必须确保不会误用it 和 mutate 不应该/不能改变的变量,否则你的代码会有问题——segfaults 很可能是其中最少的。

编辑:我忘了提到一个不会破坏类型系统的建议解决方案。只要您愿意放弃纯度,就可以使用某种全局变量(无论是在模块范围内、类变量还是结构变量)来保存缓存值。 const 函数可以自由使用和修改全局变量,因此可以使用它来代替缺失的 mutable .然而,这确实意味着函数不能是纯函数,这也可能是一个大问题。然而,这是一种让 const 函数仍然能够在不破坏类型系统的情况下改变它需要的数据的方法。

关于constants - D 中的逻辑常量,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/4219600/

相关文章:

c++ - extern const char* const SOME_CONSTANT 给我链接器错误

D 中的结构需要零初始化吗?

pointers - D: 似乎无法创建一个 std.container.Array of const struct pointers

scala - 将案例类用于可变状态是否(真的)不好?

c++ - C++ 中 const 指针可变

c++ - 无法将此指针从 const Class<T> 转换为 Class<T>&

C++:使用 Visual Studio 编译器时的常量指针

c++ - C++ 中的 extern 和 const

d - 开始使用 vibe.d

android - 将不可变位图文件转换为可变位图