【问题标题】:Is it legal to cast this to `Derived*` in the base ctor-initialiser?在基础 ctor-initialiser 中将其强制转换为 `Derived*` 是否合法?
【发布时间】:2013-02-04 13:05:13
【问题描述】:

给出以下 CRTP 示例:

template <typename T>
int foo(T* const)
{
   return 0;
}

template <typename Derived>
struct Base
{
   Base() : bar(foo(static_cast<Derived*>(this)) {};
   int bar;
};

struct Derived1 : Base<Derived1> {};

thisDerived* 的转换在这里有效吗?我记得好像不是,但现在找不到具体的证据。

现阶段this的“自然”类型是Base* const,当然在某些情况下即使在初始化过程中静态转换this指针也是不行的,比如向上转换 em> 在基地建设完成之前 (12.7/3)。

@DeadMG 说:

标准 w.r.t 中有一个明确的例外。在初始化列表中获取它。它用于将指向您自己的指针传递给子对象。

12.6.2/12 确实说:

[ 注意:因为 mem-initializer 是在构造函数的范围内计算的,所以 this 指针可以在 mem-initializer 的表达式列表中用于引用正在初始化的对象。 ——尾注]

...虽然这不足以说明到Derived* 的转换是有效的。

我的直觉是,在对象初始化的这个阶段,this 确实 指向 Derived 的实例,因此,甚至只是 拥有 类型为Derived* 的指针严格来说是UB。那是因为它既不是有效指针也不是空指针。

(这可能对this 之类的方法产生实际影响,尽管在那个答案和我上面的示例中,整个事情可以通过简单地写static_cast&lt;Derived*&gt;(0) 来回避。)

【问题讨论】:

  • afaik 强制转换和存储结果是合法的,但在派生的 ctor 运行之前不要使用它。这方面的信息可能分散在标准中,因为我现在手头没有...
  • 其实连编译都需要static_cast&lt;Derived* const&gt;(this)(编辑:nope!
  • 转换是合法的,派生子对象构造前后的对象地址不会改变,所以我看不出有什么理由禁止这样做。当然,只要foo() 不取消引用指针。
  • 好吧,3.8/5 似乎表明我错了。
  • @LightnessRacesinOrbit:不,静态转换只是创建一个新的指针对象,如果你想将它转换为指向指针的引用,那么你可能需要它作为 const 的引用。

标签: c++ language-lawyer


【解决方案1】:

我认为是UB。

正如迈克所说:

此时,已为完整对象分配存储空间,但仅初始化了基本子对象。因此,您只能以 C++11 中描述的“有限方式”使用对象的其余部分。

我的解释是这不是其中一种方式。

更正式地说:

[C++11: 3.8/1]: 对象的生命周期是对象的运行时属性。 如果一个对象是一个类或聚合类型,并且它或它的一个成员是由一个普通默认构造函数以外的构造函数初始化的,则称该对象具有非普通初始化。 [ 注意: 由平凡的复制/移动构造函数初始化是不平凡的初始化。 — 结束说明 ] T 类型对象的生命周期开始于:

  • 获得了具有适合T类型的正确对齐和大小的存储,并且
  • 如果对象有非平凡初始化,则其初始化完成。

和:

[C++11: 3.8/5]: 在对象的生命周期开始之前但在对象将占用的存储空间已分配之后,或者在对象的生命周期结束之后并且在对象的存储空间之前占用被重用或释放,任何指向对象将要或曾经位于的存储位置的指针都可以使用,但只能以有限的方式使用。对于正在构造或销毁的对象,请参阅 12.7。否则, 这样的指针指的是分配的存储(3.7.4.2),并且使用指针就像指针是void* 类型一样,是明确定义的。这样的指针可以被取消引用,但生成的左值只能以有限的方式使用,如下所述。 如果出现以下情况,则程序具有未定义的行为:

  • 对象将是或曾经是具有非平凡析构函数的类类型,并且指针用作 delete-expression 的操作数,
  • 指针用于访问非静态数据成员或调用对象的非静态成员函数,或
  • 指针被隐式转换 (4.10) 为指向基类类型的指针,或者
  • 指针用作static_cast 的操作数 (5.2.9)(转换为void*void* 并随后转换为char* 或@987654330 时除外@),或
  • 指针用作dynamic_cast 的操作数[..]

【讨论】:

  • 除了this用作static_cast的操作数时,它指向的是完全初始化的基础子对象。所以(至少通过我对那段文章的阅读)转换是明确定义的。
  • @MikeSeymour:this 指针“指的是[完整]对象将在或所在的存储位置”
  • @MikeSeymour:但这部分也适用于我认为的完整对象:“任何指向对象或所在的存储位置的指针都可能是使用但仅限于有限的方式”
  • 我最近查看了第 3.8/5 节,并认为未定义行为列表仅适用于它不是正在构建或销毁的对象。也许这一段措辞很糟糕,但这就是我所理解的。问题中的对象不是还在建设中吗?
  • 好的,我想你需要一个比我更迂腐的语言律师来解释标准的这一部分。
猜你喜欢
  • 1970-01-01
  • 2017-01-20
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2017-03-30
  • 2018-09-11
  • 1970-01-01
  • 2015-03-27
相关资源
最近更新 更多