在基ctor初始化程序中将其强制转换为“Derived*”合法吗

Is it legal to cast this to `Derived*` in the base ctor-initialiser?

本文关键字:Derived 转换 程序 初始化 ctor 在基      更新时间:2023-10-16

给定以下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指针也是不好的,例如在基础结构完成之前(12.7/3)向上投射

@DeadMG表示:

在标准w.r.t.中有一个显式异常,在初始值设定项列表中获取该值。它用于将指向自己的指针传递给子对象。

12.6.2/12确实说明:

[注意:因为mem初始值设定项是在构造函数的作用域中计算的,所以this指针可以在mem初始设定项的表达式列表中用来引用正在初始化的对象。--end Note]

尽管这不足以说明到CCD_ 6的转换是有效的。

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

(这可能会对这样的方法产生实际影响,尽管在这个答案和我上面的例子中,整个事情可以通过简单地编写static_cast<Derived*>(0)来实现。)

我认为是UB。

正如Mike所说:

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

我的解释是,这不是那种方式。

更正式的:

[C++11: 3.8/1]:对象的生存期是该对象的运行时属性如果一个对象是类或聚合类型,并且它或它的一个成员是由非平凡默认构造函数的构造函数初始化的,则称它具有非平凡初始化[注意:通过琐碎的复制/移动构造函数进行的初始化是非琐碎的初始化--结束语]类型为T的对象的生存期从以下时间开始:

  • 获得具有用于类型T的适当对准和大小的存储器,并且
  • 如果对象进行了非平凡的初始化,那么它的初始化就完成了

和:

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

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