次のことを考慮してください。
int i = 3;
i
はオブジェクトであり、タイプはint
です。履歴書の資格がありません(const
または、、volatile
またはその両方ではありません)。
ここで、次を追加します。
const int& j = i;
const int* k = &i;
j
はを参照する参照であり、はを指すi
ポインタk
ですi
。(これからは、「参照」と「ポイント」を単に「ポイント」に組み合わせます。)
この時点で、2つのcv修飾変数がj
ありk
、は、非cv修飾オブジェクトを指します。これは§7.1で言及されています。5.1/3:
cv修飾型へのポインターまたは参照は、実際にcv修飾オブジェクトを指し示したり参照したりする必要はありませんが、そうであるかのように扱われます。参照されているオブジェクトが非constオブジェクトであり、他のアクセスパスを介して変更できる場合でも、const修飾アクセスパスを使用してオブジェクトを変更することはできません。[注:cv-qualifiersは型システムでサポートされているため、キャストせずに破壊することはできません(5.2.11)。]
これが意味することは、コンパイラーはそれを尊重しj
、k
cv修飾されていないオブジェクトを指している場合でも、cv修飾されている必要があるということです。(つまりj = 5
、合法*k = 5
であるにもかかわらず、違法i = 5
です。)
const
ここで、それらからを削除することを検討します。
const_cast<int&>(j) = 5;
*const_cast<int*>(k) = 5;
これは合法です(§5.2.11を参照)が、未定義の動作ですか?いいえ。§7.1を参照してください。5.1/4:
可変(7.1.1)と宣言されたクラスメンバーを変更できることを除いて、constオブジェクトをその存続期間(3.8)中に変更しようとすると、未定義の動作が発生します。
強調鉱山。
i
それはそうではなく const
、それと両方がを指していることj
を忘れないでください。私たちが行ったのは、型システムに型からconst-qualifierを削除するように指示することだけです。これにより、ポイントされたオブジェクトを変更し、それらの変数を使用して変更できます。k
i
i
これは、次のこととまったく同じです。
int& j = i; // removed const with const_cast...
int* k = &i; // ..trivially legal code
j = 5;
*k = 5;
そして、これは自明に合法です。i
代わりに、これがこれであると考えます。
const int i = 3;
今の私たちのコードは何ですか?
const_cast<int&>(j) = 5;
*const_cast<int*>(k) = 5;
はconst修飾オブジェクトであるため、未定義の動作につながるようになりました。型システムに、ポイントされたオブジェクトを変更できるようi
に削除するように指示してから、const修飾オブジェクトを変更しました。上で引用したように、これは未定義です。const
繰り返しますが、より明白なのは:
int& j = i; // removed const with const_cast...
int* k = &i; // ...but this is not legal!
j = 5;
*k = 5;
単にこれを行うことに注意してください:
const_cast<int&>(j);
*const_cast<int*>(k);
const修飾オブジェクトは変更されていないため、完全に合法で定義されています。型システムをいじっているだけです。
今考えてみましょう:
struct foo
{
foo() :
me(this), self(*this), i(3)
{}
void bar() const
{
me->i = 5;
self.i = 5;
}
foo* me;
foo& self;
int i;
};
メンバーはどうしconst
ますか?それらへのアクセスは、cv修飾アクセスパスbar
と呼ばれるものを経由します。(これは、のタイプをからに変更することによって行われます。ここで、は関数のcv修飾子です。)this
T* const
cv T const*
cv
では、実行中のメンバータイプは何bar
ですか?彼らです:
// const-pointer-to-non-const, where the pointer points cannot be changed
foo* const me;
// foo& const is ill-formed, cv-qualifiers do nothing to reference types
foo& self;
// same as const int
int const i;
もちろん、重要なのはポインタではなく、オブジェクトを指すconst-qualificationであるため、タイプは関係ありません。(k
上記const int* const
の場合、後者const
は関係ありません。)ここで次のことを検討します。
int main()
{
foo f;
f.bar(); // UB?
}
内bar
では、me
とself
は非定数を指しているfoo
ため、int i
上記と同様に、明確に定義された動作があります。私たちが持っていた:
const foo f;
f.bar(); // UB!
const int
const修飾オブジェクトを変更するので、と同じようにUBがあります。
あなたの質問では、const修飾オブジェクトがないため、未定義の動作はありません。
そして、権威に訴えるためconst_cast
に、非const関数でconst修飾関数をリサイクルするために使用されるScottMeyersによるトリックを考えてみましょう。
struct foo
{
const int& bar() const
{
int* result = /* complicated process to get the resulting int */
return *result;
}
int& bar()
{
// we wouldn't like to copy-paste a complicated process, what can we do?
}
};
彼は提案します:
int& bar(void)
{
const foo& self = *this; // add const
const int& result = self.bar(); // call const version
return const_cast<int&>(result); // take off const
}
またはそれが通常どのように書かれているのか:
int& bar(void)
{
return const_cast<int&>( // (3) remove const from result
static_cast<const foo&>(*this) // (1) add const to this
.bar() // (2) call const version
);
}
これもまた、完全に合法であり、明確に定義されていることに注意してください。具体的には、この関数は非const修飾で呼び出される必要があるためfoo
、戻り型の。からconst修飾を取り除くことは完全に安全ですint& boo() const
。
const_cast
(誰かが最初に+呼び出しで自分自身を撃たない限り。)
要約する:
struct foo
{
foo(void) :
i(),
self(*this), me(this),
self_2(*this), me_2(this)
{}
const int& bar() const
{
return i; // always well-formed, always defined
}
int& bar() const
{
// always well-formed, always well-defined
return const_cast<int&>(
static_cast<const foo&>(*this).
bar()
);
}
void baz() const
{
// always ill-formed, i is a const int in baz
i = 5;
// always ill-formed, me is a foo* const in baz
me = 0;
// always ill-formed, me_2 is a const foo* const in baz
me_2 = 0;
// always well-formed, defined if the foo pointed to is non-const
self.i = 5;
me->i = 5;
// always ill-formed, type points to a const (though the object it
// points to may or may not necessarily be const-qualified)
self_2.i = 5;
me_2->i = 5;
// always well-formed, always defined, nothing being modified
// (note: if the result/member was not an int and was a user-defined
// type, if it had its copy-constructor and/or operator= parameter
// as T& instead of const T&, like auto_ptr for example, this would
// be defined if the foo self_2/me_2 points to was non-const
int r = const_cast<foo&>(self_2).i;
r = const_cast<foo* const>(me_2)->i;
// always well-formed, always defined, nothing being modified.
// (same idea behind the non-const bar, only const qualifications
// are being changed, not any objects.)
const_cast<foo&>(self_2);
const_cast<foo* const>(me_2);
// always well-formed, defined if the foo pointed to is non-const
// (note, equivalent to using self and me)
const_cast<foo&>(self_2).i = 5;
const_cast<foo* const>(me_2)->i = 5;
// always well-formed, defined if the foo pointed to is non-const
const_cast<foo&>(*this).i = 5;
const_cast<foo* const>(this)->i = 5;
}
int i;
foo& self;
foo* me;
const foo& self_2;
const foo* me_2;
};
int main()
{
int i = 0;
{
// always well-formed, always defined
int& x = i;
int* y = &i;
const int& z = i;
const int* w = &i;
// always well-formed, always defined
// (note, same as using x and y)
const_cast<int&>(z) = 5;
const_cast<int*>(w) = 5;
}
const int j = 0;
{
// never well-formed, strips cv-qualifications without a cast
int& x = j;
int* y = &j;
// always well-formed, always defined
const int& z = i;
const int* w = &i;
// always well-formed, never defined
// (note, same as using x and y, but those were ill-formed)
const_cast<int&>(z) = 5;
const_cast<int*>(w) = 5;
}
foo x;
x.bar(); // calls non-const, well-formed, always defined
x.bar() = 5; // calls non-const, which calls const, removes const from
// result, and modifies which is defined because the object
// pointed to by the returned reference is non-const,
// because x is non-const.
x.baz(); // well-formed, always defined
const foo y;
y.bar(); // calls const, well-formed, always defined
const_cast<foo&>(y).bar(); // calls non-const, well-formed,
// always defined (nothing being modified)
const_cast<foo&>(y).bar() = 5; // calls non-const, which calls const,
// removes const from result, and
// modifies which is undefined because
// the object pointed to by the returned
// reference is const, because y is const.
y.baz(); // well-formed, always undefined
}
ISO C++03規格を参照しています。