【问题标题】:Applying const_cast on this-> pointer在 this-> 指针上应用 const_cast
【发布时间】:2014-08-12 12:21:38
【问题描述】:

我正在使用一些代码来删除变量的恒定性。

  int *i = new int(202);
  int *j = new int(402);

  int *const iptr = i;    // Constant pointer
  //iptr = j ;           // Not allowed. Constant pointer cannot point to another location.
  (*iptr)++;             // Allowed
  const_cast<int *>(iptr) = j;
  cout<< *iptr           // prints 402

它按预期工作,但是当我尝试删除“this”指针的恒定性时,编译器不允许这样做,即它在 const_cast 语句下方显示波浪线。

class A
{
public:
  A(A * obj)
  {
    const_cast<A *>(this) = obj; 
  }
};

当我将鼠标(我使用 VS2014)悬停在早期代码中的“this”和“iptr”上时,我可以看到类型相同,即 &lt;classname&gt; *const

谁能解释一下幕后发生了什么?

干杯,

【问题讨论】:

  • iptr 是一个 const 对象。在任何情况下修改它都是未定义的行为。所以,const_cast&lt;int *&gt;(iptr) = j; 在语义上是错误的(这并不奇怪——你在对编译器撒谎。)
  • 我们应该添加一个功能,自动回复 5k 代表(是的,包括我)以下用户的 const_castreinterpret_cast 问题:不要碰这个,你会伤害自己!
  • 您要分配给this?有趣,而且完全违法。 this 被标准定义为纯右值表达式(prvalue = pure rvalue)。它不能出现在作业的左侧。 this = .... 是不合法的,并且铸造不会改变这一点。参见 C++11 § 9.3.2,p1。 [class.this]。
  • 在任何情况下都不能赋值给this,它的行为就像一个字面量。
  • cout&lt;&lt; *iptr; // prints 402 没有按预期工作,因为没有预期,因为它是未定义的行为。进行了一些优化的编译器将在其输出 203 的权利范围内

标签: c++ pointers this const-cast


【解决方案1】:

恐怕你不明白const_cast是干什么用的。

在 C++ 中,const 用于两种情况:

  • 不能更改const 对象的逻辑值(并且,除了mutable 字段,它的按位值也不能更改)
  • const*const&amp; 是只读指针或引用

const_cast 不是改变const 对象,而是通过只读指针或引用来修改非常量对象

请记住,虽然射击自己的脚很容易,因为当您得到const&amp; 时,您如何知道原始对象是否为const?你不。如果你试图改变一个 const独角兽出现,或者可能是魔鬼(也称为未定义行为:任何事情都可能发生)。

现在,与this 的关联很棘手。严格来说this 是一个右值(意味着它只能按原样出现在=R 右手侧),尽管它通常被简单描述为const为简单起见,所以在A 的构造函数中,this 被描述为具有A* const 类型。值得庆幸的是,即使在这种近似下,const_cast 也是一个坏主意,因为原始对象是 const


因此,建议是:

  • Junior:不要使用 const_castreinterpret_cast 或 C 风格的类型转换(因为当它们脱糖到前两者之一时不明显)。
  • 前辈:你经验不够。
  • 专家:加油!你怎么能声称自己是专家并尝试使用它们?

【讨论】:

  • 另外,this 是右值而不是左值,因此它只能用作函数的参数,不能存储结果或获取引用/指针
  • 谢谢。我只是想知道为什么编译器首先允许而不是当我尝试使用这个指针时。
  • @pqnet: 好点,我忘了在中途寻址this;已更正。
  • @MatthieuM。是的,但恰恰相反,即this 不是l-value,而是r-valuethis 不能出现在 = 的左侧
  • @pqnet: 没错,我的左右混在一起了:x
【解决方案2】:

this 不是左值。

你不能将它分配给其他东西。

您可以使用*this = *obj;,这不需要const_cast

您可以使用const_cast&lt;A*&gt;(this) 来覆盖 constness,并且与任何其他 const_cast 一样充满危险,但它可以让您执行 const/非 const 重载,而不必复制实现,例如

T& A::get()
{
 // some complex code to find the right reference, assigning to t
  return t;
}

const T& A::get() const
{
// implement in terms of above function
  return (const_cast<A*>(this))->get(); // invokes above
 // and automatically converts the reference to const
}

【讨论】:

  • 我认为这个例子是错误的。 const/non-const 重载技巧只能反过来安全地使用。非 const 版本需要调用 const 版本,然后适当地转换 const 版本的返回值。因为非常量版本知道它是在非常量对象上调用的,所以它也可以调用常量版本。 const 版本理论上可以在只读存储中的对象上调用,然后调用非 const 版本可能会崩溃。
  • 如果你已经实现了非常量版本并且知道它不会修改任何东西,只是它返回一个非常量引用,那么我的代码是安全的。但是是的,你可以反过来做,无论哪种方式你最终都会得到一个 const_cast。
  • 如果你实现了一个非常量版本并且它没有修改任何东西,那么它就不是 non-const 版本,你应该将它声明为 const 以便编译器可以检查你的假设:-)
  • 它不会修改任何内容,而是返回对要求函数为非常量的成员变量的引用。是的,你可以反过来实现它,我可能会,但我想举一个例子,在这个例子中 const_cast 可能是合理的。 “以相反的方式实现”是 const_cast 完全合理的情况之一,通常应该避免。
  • 顺便说一句,LLVM codebase 中恰好有这个结构:1) "CXRecordDecl *getCanonicalDecl() override { return cast(RecordDecl::getCanonicalDecl()); }" And 2) " const CXXRecordDecl *getCanonicalDecl() const { return const_cast(this)->getCanonicalDecl(); }"
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-03-05
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-06-15
相关资源
最近更新 更多