考虑以下:
int i = 3;
i
是一个对象,它的类型是int
. 它不是 cv 限定的(不是const
or volatile
,或两者兼而有之。)
现在我们添加:
const int& j = i;
const int* k = &i;
j
是指向 的引用i
,并且k
是指向 的指针i
。(从现在开始,我们简单地将“引用”和“指向”组合成“指向”。)
此时,我们有两个 cv 限定变量,j
和k
,它们指向一个非 cv 限定对象。§7.1.5.1/3 中提到了这一点:
指向 cv 限定类型的指针或引用不需要实际指向或引用 cv 限定对象,但它被视为好像确实如此;即使引用的对象是非常量对象并且可以通过其他访问路径进行修改,也不能使用 const 限定的访问路径来修改对象。[注意:类型系统支持 cv 限定符,因此如果不进行强制转换(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:
除了可以修改任何声明为 mutable (7.1.1) 的类成员之外,任何在 const 对象的生命周期 (3.8) 期间修改它的尝试都会导致未定义的行为。
强调我的。
请记住 that i
is not const
和 thatj
和k
两者都指向i
。我们所做的只是告诉类型系统从类型中删除 const 限定符,以便我们可以修改指向的对象,然后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;
它现在导致未定义的行为,因为i
它是一个 const 限定的对象。我们告诉类型系统删除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
对会员做什么bar
?它使对它们的访问通过称为cv-qualified access path 的方式进行。(它通过将this
from的类型更改为T* const
来做到这一点,函数上的 cv 限定符在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 限定,而不是指针。(k
以上是const int* const
,后者const
无关紧要。)我们现在考虑:
int main()
{
foo f;
f.bar(); // UB?
}
在bar
,me
和都self
指向一个非常量foo
,所以就像int i
上面一样,我们有明确定义的行为。如果我们有:
const foo f;
f.bar(); // UB!
我们会有 UB,就像 with 一样const int
,因为我们将修改一个 const 限定的对象。
在您的问题中,您没有 const 限定的对象,因此您没有未定义的行为。
只是为了增加对权威的吸引力,请考虑const_cast
Scott Meyers 的技巧,用于在非常量函数中回收 const 限定函数:
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-qualified 上调用foo
,所以我们从 的返回类型中剥离 const-qualification 是完全安全的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 标准。