13

Dには2つのタイプの不変性があります。不変変数は不変であると宣言されたものであり、常に不変ですが、const変数は単にオブジェクトの読み取り専用バージョンです。

論理constは、関数がconstとしてマークされている場合ですが、1つ以上のメンバー変数への書き込みアクセスを許可します。これの典型的な使用法は、遅延評価です。たとえば、(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_determinantmutable

D const(FAQ)は、D2が提供する保証が弱いため、論理constをサポートしないと述べています。これは、並行プログラムの作成の妨げとなり、特定の最適化をより困難にします。

私は懸念を完全に理解していますが、論理定数が必要な場合はどうなりますか?

上記の場合をクラスで考えてみましょうMatrix。ただし、キャッシュはありません(そして論理定数は必要ありません)。また、このクラスが私のコードベース全体で使用されており、ほとんどがconst参照を介してアクセスされていることを想像してみてください。

ここで、プロファイリングにより、determinant()関数がコードのボトルネックであることが明らかになり、さらに、通常は値が変更されることはめったになく、繰り返しアクセスされます。つまり、上記のように、キャッシュは完全に最適化されます。

論理定数なしでそれを行うにはどうすればよいですか?コードベース全体に行き、const参照をnon-const参照に変更することは、オプションではありません(明らかな理由から)。

(もしあれば)どのようなオプションがありますか?

4

5 に答える 5

11

このトピックに関する最近のスレッドの基本的な結論をここの D ニュースグループに投稿するのが適切だと思います。そうすれば、そのリストを追跡していない人でも適切な回答を得ることができます。

D の const は論理 const ではありません。これは推移的であり、完全に const です。この言語は、論理定数を技術的にサポートしていません。この言語は、const オブジェクトを変更する方法を定義していません。

実際、C++ には論理定数もありません。const ネスを使用mutableしてキャストすることで、const を完全に回避できます。技術的に言えば、const は、const 変数で非 const 関数を呼び出していないことを除いて、実際には何も保証しません。const 関数が実際には const であり、変数を台無しにしないという事実は、慣習によって完全にまとめられています。現在、ほとんどのプログラマーは const ネスを左と右にキャストしてすべてを変更可能にすることはありません。したがって、実際には非常に便利ですが、完全に回避できるだけでなく、言語は特にそうするための定義された手段を提供します。 . C++mutableおよびキャスト アウェイ const では、言語によって明確に定義され、サポートされています。

Dはそれをしません。D の const は実際には const です。変数の const をキャストしてから変更することは未定義です。ミュータブルはありません。D の const には本当の保証があります (何かの const をキャスト アウェイしてから変更するなど、未定義のことをしない限り)。これは重要です。なぜなら、D に対するコンパイラの保証が C++ に対するコンパイラの保証よりもはるかに強力だからだけでなく、不変変数はできないからです。形や形を変えることはできません。それらは読み取り専用メモリにある可能性があり、不変性を捨ててそのような変数を変更しようとすると、どんな恐ろしいことが起こるか誰にもわかりません (segfault は、発生する可能性のある最も良いことでしょう)。また、const 変数は実際には不変データを参照する可能性があるため、const をキャストして変数を変更したり、const 変数を何らかの方法で変更できるようにすることは、控えめに言っても悪いことです。したがって、言語はそれを許可しません。

さて、BCS が指摘しているように、D は実用的な言語です。const をキャストすることができ、その時点で変数を変更できます。したがって、たとえば、const 関数の戻り値をキャッシュするために使用される変数 (オブジェクトの状態が変化した場合、おそらくそのキャッシュが無効になる) を使用し、const をキャストして変更することができます。問題の変数が実際に不変でない限り、それは機能します。ただし、これは未定義の動作です。それをしたら、あなたは自分自身です。型システムとコンパイラの保証をバイパスしています。あなたは、不変オブジェクトでそれを行わないようにするか、コンパイラが通常保証するものを台無しにしないようにする責任があります。したがって、必要な場合はそうすることはできますが、ワイルド ウェストに足を踏み入れているのです。必要でないものを変異させないようにするのはあなた次第です。

変数が実際に不変データを参照しない限り、const のキャスト アウェイが機能することを考えると、基本的に C++ でMutable得られるものを取得するためのテンプレートを作成することができます (つまり、const-ness のキャスト アウェイを行います)。mutableあなたのために)。he_the_great は、回答でそのようなテンプレートの例を示しています。しかし、そのようなテンプレートの使用はまだ未定義の動作です。実際に不変であるオブジェクトでそれを使用すると、問題が発生します。プログラマであるあなたは、それが正しく使用されていることを確認する必要があります。

したがって、D は const をキャストすることで論理的な const を持つことを技術的に可能にしますが、それを行うには、型システムをバイパスしてコンパイラが保証するものの外に出る必要があり、誤用しないようにする必要があります。それを変更して、変更してはならない/変更できない変数を変更します。そうしないと、コード問題が発生します-セグメンテーション違反がそれらの中で最も少ない可能性があります。

編集:型システムを壊さない提案された解決策について言及するのを忘れていました。純粋性を放棄しても構わないと思っている限り、何らかの種類のグローバル変数 (モジュール スコープ、クラス変数、構造体変数など) を使用して、キャッシュされた値を保持できます。const 関数は、グローバル変数を自由に使用および変更できるため、欠落している の代わりに使用できますmutable。ただし、これは関数が純粋ではないことを意味し、これも大きな問題になる可能性があります。ただし、型システムを壊すことなく、const 関数が必要なデータを変更できるようにする方法です。

于 2010-12-01T19:51:45.397 に答える
4

私は何年も D2 に触れていないので、私の言うことを再確認してください。:)

本当に良い選択肢があるかどうかはわかりません。D の const と immutable は C/C++ よりもはるかに強力であるため、それらをキャストすることはできません。コードで const の使用法を変更することを明示的に除外しました。

操作の結果を、マトリックス値自体をキーとするグローバル ハッシュテーブルにキャッシュできます。これは、const/immutable の任意の組み合わせで機能します。もちろん、これに関する問題は、D には世界最速のハッシュテーブルがなく、ハッシュの計算が遅くなる可能性があることです。マトリックスを作成するときに、ハッシュを事前に計算する場合があります。

もう 1 つのオプションは、値が変化したときに行列式を熱心に計算することです。

それ以外に、私は何も考えられません。問題は、実際には、コンパイラに const で保護するように要求し、それから抜け出そうとしていることです。「適切な」解決策は、おそらく const を使用しないことです。:P

于 2010-11-19T05:21:00.210 に答える
1

実用的な言語である D には、本当に必要な場合に const をキャストする機能があります。私は以下がうまくいくはずだと思います:

class M {
  bool set;
  real val;

  real D() const {
    if(!set) {
      M m = cast(M)this;
      m.val = this.some_fn();
      m.set = true;
    }
    return this.val;
  }
}
于 2010-11-19T01:24:30.007 に答える
0

D で C++ の論理を模倣するconstには、クラスの継承を使用できます。

class ConstMatrix
{
public:
    double det() { // not marked as const!
        /* ... caching code ... */
    }
    /* ... the rest of the logically const interface ... */
}

class Matrix : ConstMatrix
{
public:
    void set( int row, int col, double val ) {
        /* ... */
    }
    /* ... the non-logically const interface ... */
}

クラスの実装では、関数シグネチャに修飾子をConstMatrix付けない限り、コンパイラ チェックはありません。ただし、論理的に定数の行列constに使用すると、クライアント コードの const の正確性が得られます。ConstMatrix

構造体の場合、この手法を使用しaliasて同じことを達成できます。

struct ConstMatrix 
{
    /* logically const bla bla blub */
}

struct Matrix
{
public:
    alias m this;

    /* non-const bla bla blub */
private:
    ConstMatrix m;
}

クラス型の場合、次の方法で論理 const の正確さを使用して他のclasses またはs を構築できます。struct

class ConstBiggerClass
{
private:
    ConstMatrix m;
}

class BiggerClass : ConstBiggerClass
{
private:
    Matrix m;
}

このようにして、コンパイラはconst正確さをチェックします。ただし、追加のデータ メンバーが必要になります。class型の型メンバーの場合class、別の方法として、適切な constness を持つデータ メンバーを返すメンバー関数を提供することができます。

class ConstBiggerClass
{
public:
    void someLogicallyConstFunction( /*...*/ ) { /* ... */ }
protected:
    abstract ConstMatrix getMatrix();
}

class BiggerClass : ConstBiggerClass
{
public:
    void someMutableFunction( /*...*/ ) { /*...*/ }
protected:
    Matrix getMatrix() { return m; }
private:
    Matrix m;
}
于 2013-05-15T12:18:19.960 に答える
0

不変/定数のマトリックスが作成されない限り、BCS の回答はシンプルで安全であるため、BCS の回答を強くお勧めします。

不変/ const オブジェクトでさえも有効なままにするのに役立つ別のオプションは、このMutable Templateです。または、少なくともそれが意図です。懸念事項についてのコメントがあります。

このテンプレートでは、const 関数の値ではなく、参照される型に対して変更を行う必要があります。これは、ポインターが使用され、各マトリックスに割り当てるスペースが必要であることを意味します。これにより、セグメンテーション違反にならない不変/定数マトリックスを作成することも難しくなります。おそらくそれをうまく行う方法がありますが、私はクラス用のものしか知りません。

struct Matrix
{
    double determinant() const
    {
        if ( *m_dirty )
        {
            *m_determinant = 646.363; /* 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;
};
于 2010-11-30T21:14:09.213 に答える