将它转换为基础ctor-initialiser中的"Derived*"是否合法?

Lig*_*ica 12 c++ language-lawyer

给出以下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> {};
Run Code Online (Sandbox Code Playgroud)

被转换thisDerived*这里可能无效?我似乎记得它可能不是,但现在却找不到具体证据.

this在这个阶段的"自然"类型是Base* const,并且在某些情况下,甚至this在初始化期间甚至静态地投射指针也是不合适的,例如在基础构造完成之前的向上转换(12.7/3).

@DeadMG说:

标准中有一个显式异常,在初始化列表中获取此异常.它是将指针传递给自己的子对象.

12.6.2/12确实说:

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

...虽然这还不足以说转换Derived*为有效.

我的直觉是,在对象的初始化的这个阶段,this没有指向的一个实例Derived,并且同样地,哪怕只是它的指针型Derived*的,严格来说,UB.那是因为它既不是有效指针也不是空指针.

(这可能对等接近实际后果,虽然在这个问题的答案,整个事情我上面的例子可以通过简单地以书面形式向旁边跨步static_cast<Derived*>(0)代替.)

Lig*_*ica 4

我认为是UB。

\n\n

正如迈克所说:

\n\n
\n

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

\n
\n\n

我的解释是这不是这些方式之一。

\n\n

更正式地说:

\n\n
\n

[C++11: 3.8/1]:对象的生命周期是该对象的运行时属性。如果一个对象属于类或聚合类型,并且该对象或其成员之一是由普通默认构造函数以外的构造函数初始化的,则称该对象具有非平凡初始化。[注意:通过简单的复制/移动构造函数进行的初始化是非简单的初始化。\xe2\x80\x94\n 尾注]类型对象的生命周期T开始于:

\n\n
    \n
  • T获得具有正确对齐方式和类型大小的存储,并且
  • \n
  • 如果对象进行了重要的初始化,则其初始化已完成。
  • \n
\n
\n\n

和:

\n\n
\n

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

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