gpt4 book ai didi

c++ - 在没有 const_cast 的情况下修改 *this 的 const 方法

转载 作者:可可西里 更新时间:2023-11-01 16:25:59 25 4
gpt4 key购买 nike

我正在编写的程序中出现了以下模式。我希望它不是太做作,但它设法改变了 Foo const 方法中的对象 Foo::Questionable() const , 不使用任何 const_cast 或类似的。基本上,Foo存储对 FooOwner 的引用反之亦然,在 Questionable() , Foo设法通过调用 mutate_foo() 在 const 方法中修改自身在它的主人身上。问题遵循代码。

#include "stdafx.h"
#include <iostream>
using namespace std;

class FooOwner;

class Foo {
FooOwner& owner;
int data;

public:
Foo(FooOwner& owner_, int data_)
: owner(owner_),
data(data_)
{
}

void SetData(int data_)
{
data = data_;
}

int Questionable() const; // defined after FooOwner
};

class FooOwner {
Foo* pFoo;

public:
FooOwner()
: pFoo(NULL)
{}

void own(Foo& foo)
{
pFoo = &foo;
}

void mutate_foo()
{
if (pFoo != NULL)
pFoo->SetData(0);
}
};

int Foo::Questionable() const
{
owner.mutate_foo(); // point of interest
return data;
}

int main()
{
FooOwner foo_owner;
Foo foo(foo_owner, 0); // foo keeps reference to foo_owner
foo_owner.own(foo); // foo_owner keeps pointer to foo

cout << foo.Questionable() << endl; // correct?

return 0;
}

这是定义的行为吗?应 Foo::data被声明为可变的?或者这是否表明我正在做的事情是致命的错误?我正在尝试实现一种仅在请求时才设置的延迟初始化的“数据”,并且以下代码可以正常编译而没有警告,所以我在 UB 土地上有点紧张。

编辑: const在 Questionable() 上只使直接成员为 const,而不是对象指向或引用的对象。这是否使代码合法?我对 Questionable() 中的事实感到困惑, this有类型 const Foo* ,并进一步向下调用堆栈, FooOwner合法地有一个非常量指针用来修改 Foo .这是否意味着 Foo对象是否可以修改?

编辑 2:也许是一个更简单的例子:
class X {
X* nonconst_this; // Only turns in to X* const in a const method!
int data;

public:
X()
: nonconst_this(this),
data(0)
{
}

int GetData() const
{
nonconst_this->data = 5; // legal??
return data;
}
};

最佳答案

考虑以下:

int i = 3;
i是一个对象,它的类型是 int .它不是 cv 限定的(不是 constvolatile ,或两者。)

现在我们添加:
const int& j = i;
const int* k = &i;
j是引用 i , 和 k是指向 i 的指针. (从现在开始,我们简单地将“引用”和“指向”结合为“指向”。)

此时,我们有两个 cv 限定的变量, jk , 指向一个非 cv 限定的对象。这在 §7.1. 5.1/3 中提到:

A pointer or reference to a cv-qualified type need not actually point or refer to a cv-qualified object, but it is treated as if it does; a const-qualified access path cannot be used to modify an object even if the object referenced is a non-const object and can be modified through some other access path. [Note: cv-qualifiers are supported by the type system so that they cannot be subverted without casting (5.2.11). ]



这意味着编译器必须遵守 jk是 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:

Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior. Emphasis mine.



请记住 i不是 const还有那个 jk都指向 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;
};

什么 constbar对成员(member)做什么?它使对它们的访问通过称为 cv 限定访问路径的东西。 (它通过将 this 的类型从 T* const 更改为 cv T const* 来实现,其中 cv 是函数的 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 , 两者 meself指向非常量 foo ,就像 int i上面我们有明确定义的行为。如果我们有:
const foo f;
f.bar(); // UB!

我们会有 UB,就像 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 限定的 foo 上调用此函数,我们从 int& boo() const 的返回类型中去除 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 标准。

关于c++ - 在没有 const_cast 的情况下修改 *this 的 const 方法,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/3484233/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com