6

別の質問のいくつかの回答に対する私のコメントへの応答で、誰かが次のようなことを示唆しています

void C::f() const
{
  const_cast<C *>( this )->m_x = 1;
}

constオブジェクトが変更されたため、未定義の動作を呼び出します。これは本当ですか?そうでない場合は、これを許可するC ++標準を引用してください(どの標準から引用するかを記載してください)。

mutable価値があるのは、 1つまたは2つのメソッドだけでメンバー変数に書き込む必要がある場合にメンバー変数を作成しないようにするために、常にこのアプローチを使用してきました(を使用すると、すべてのmutableメソッドに書き込み可能になるため)。

4

3 に答える 3

11

constオブジェクト(C++11では7.1.6.1/4)を変更する(しようとする)のは未定義の動作です。

したがって、重要な質問は、constオブジェクトとは何であり、m_x1つであるかということです。もしそうなら、あなたはUBを持っています。そうでない場合は、UBであることを示すものは何もありません。もちろん、ここに示されていない他の理由(データ競合など)でUBである可能性があります。

関数fがクラスのconstインスタンスで呼び出された場合Cm_x constオブジェクトであるため、動作は未定義です(7.1.6.1/5)。

const C c;
c.f(); // UB

関数fがクラスの非constインスタンスで呼び出された場合Cm_xはconstオブジェクトではないため、動作は私たちが知る限り定義されています。

C c;
const C *ptr = &c;
c->f(); // OK

したがって、この関数を作成する場合は、constインスタンスを作成しCて関数を呼び出さないように、ユーザーに翻弄されます。おそらく、のインスタンスはC一部のファクトリによってのみ作成されます。その場合、それを防ぐことができます。

完全なオブジェクトがである場合でもデータメンバーを変更可能にする場合はconst、それをマークする必要がありますmutable。それが目的であり、のconstインスタンスで呼び出されたmutable場合でも、定義された動作を提供します。fC

C ++ 11以降、constメンバー関数とmutableデータメンバーの操作はスレッドセーフである必要があります。そうしないと、タイプが標準ライブラリの関数およびコンテナで使用されている場合に、標準ライブラリによって提供される保証に違反します。

したがって、C ++ 11ではm_x、アトミックタイプを作成するか、他の方法で変更を同期するか、constとマークされていても、関数fがスレッドセーフではないという最後の手段としてドキュメントを作成する必要があります。これらのことを何もしなかった場合は、ユーザーが機能するはずであると合理的に信じているが実際にはUBを持っているコードを書く機会を再び作成します。

于 2013-01-04T10:09:33.640 に答える
8

2つのルールがあります:

  1. constオブジェクトを変更することはできません。

  2. constポインタまたは参照を介してオブジェクトを変更することはできません。

基になるオブジェクトがconstでない場合は、どちらのルールにも違反しません。オブジェクトへのconstポインタまたはconst参照が存在すると、そのオブジェクトが変更または変更されないようにするという一般的な誤解があります。それは単に誤解です。例えば:

#include <iostream>
using namespace std;

// 'const' means *you* can't change the value through that reference
// It does not mean the value cannot change

void f(const int& x, int* y)
{
    cout << "x = " << x << endl;
    *y = 5;
    cout << "x = " << x << endl;
}

int main()
{
    int x = 10;
    f(x, &x);
}

キャストがいないことに注意してください。面白いことは何もありません。ただし、関数がconst参照を持っているオブジェクトは、その関数によって変更されます。それは許可されています。あなたのコードは同じです、それはただconstnessを捨てることによってそれをします。

ただし、基になるオブジェクトがconstの場合、これは不正です。たとえば、このコードは私のマシンでsegfaultsします。

#include <iostream>
using namespace std;

const int i = 5;

void cast(const int *j)
{
    *const_cast<int *>(j) = 1;
}

int main(void)
{
    cout << "i = " << i << endl;
    cast(&i);
    cout << "i = " << i << endl;
}

セクション3.4.3(CV修飾子)および5.2.7(恒常性を捨てる)を参照してください。

于 2013-01-04T09:32:07.640 に答える
-1

これ以上検索せずに、C++11標準の§1.9/4は次のようになります。

他の特定の操作は、この国際規格では未定義として説明されています(たとえば、constオブジェクトを変更しようとした場合の影響)。

そして、これはあなたがここでやろうとしていることです。constnessをキャストしていることは問題ではありません(これを行わなかった場合、動作は明確に定義されています。コードはコンパイルに失敗します)。オブジェクトを変更しようとしているconstため、未定義の動作が発生しています。

多くの場合、コードは機能しているように見えます。ただし、呼び出しているオブジェクトが実際constに存在し、ランタイムが読み取り専用メモリに格納することを決定した場合は、そうではありません。このオブジェクトが元々ではなかったことが本当に確実でない限り、恒常性を捨てることは危険ですconst

于 2013-01-04T09:54:45.053 に答える