考虑以下事项:
int i = 3;
i
是一个对象,它具有
int
. 不合格(不合格)
const
或
volatile
,或者两者兼而有之。
现在我们添加:
const int& j = i;
const int* k = &i;
j
是指
我
和
k
是指向的指针
我
. (从现在开始,我们只需将“引用”和“指向”组合为“指向”。)
在这一点上,我们有两个Cv限定变量,
J
和
K
,指向一个非CV限定对象。这在7.1.5.1/3节中提到:
指向cv限定类型的指针或引用不需要实际指向或引用cv限定对象,但将其视为指向或引用;即使引用的对象是非const对象,并且可以通过其他访问路径进行修改,也不能使用const限定访问路径来修改对象。[注:cv限定符受类型系统支持,因此它们在没有强制转换(5.2.11)的情况下不能被转换。]
这意味着编译器必须尊重
J
和
K
是合格的简历,即使他们指向一个非合格的简历对象。(所以)
j = 5
和
*k = 5
是非法的,即使
i = 5
是合法的。)
我们现在考虑移除
康斯特
从那些:
const_cast<int&>(j) = 5;
*const_cast<int*>(k) = 5;
这是合法的(§参考5.2.11),但它是未定义的行为吗?
不。
见7.1.5.1/4:
除了声明为可变的类成员(7.1.1)可以修改外,
在const对象的生存期(3.8)内,任何修改const对象的尝试都会导致未定义的行为。
.
强调我的。
记住
我
是
不
康斯特
那
J
和
K
都指向
我
. 我们所做的就是告诉类型系统从类型中移除const限定符,这样我们可以修改指向对象,然后修改
我
通过这些变量。
这与执行以下操作完全相同:
int& j = i; // removed const with const_cast...
int* k = &i; // ..trivially legal code
j = 5;
*k = 5;
这是非常合法的。我们现在考虑一下
我
而是这样:
const int i = 3;
我们现在的代码是什么?
const-cast<int>(j)=5;
*常量cast<int*>(k)=5;
现在导致
未定义的行为
,因为
我
是常量限定对象。我们告诉类型系统删除
康斯特
所以我们可以修改指向对象,
然后修改一个常量限定对象
. 如上文所述,这是未定义的。
同样,更明显的是:
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);
这是完全合法和定义的,因为没有修改常量限定对象;我们只是在处理类型系统。
现在考虑:
struct foo
{
foo() :
me(this), self(*this), i(3)
{}
void bar() const
{
me->i = 5;
self.i = 5;
}
foo* me;
foo& self;
int i;
};
什么?
康斯特
在
bar
对会员做什么?它使访问它们的权限通过一个称为
cv限定访问路径
. (它通过改变
this
从
T* const
到
cv T const*
在哪里
cv
是函数上的cv限定符。)
那么在执行
酒吧
?他们是:
// 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;
当然,类型是不相关的,因为重要的是
指向
对象,而不是指针。有
K
以上已
const int* const
后者
康斯特
我们现在考虑:
int main()
{
foo f;
f.bar(); // UB?
}
内
酒吧
,两者
me
和
self
指向非常量
foo
就像和
int i
上面我们有明确的行为。我们曾经拥有过:
const foo f;
f.bar(); // UB!
我们会有UB,就像
const int
,因为我们将修改一个常量限定的对象。
在您的问题中,您没有常量限定对象,因此没有未定义的行为。
为了增加对权威的吸引力,考虑一下
const_cast
Scott Meyers的技巧,用于在非常量函数中循环常量限定函数:
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限定条件。
int& boo() const
.
(除非有人用
康斯特卡斯特
+首先打电话。)
总结:
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标准。