C++ 虚拟继承 : static_cast "this" to virtual parent in initializer list of derived

标签 c++ constructor language-lawyer lifetime virtual-inheritance

我有一些代码。它不起作用。

首先,您会看到这个示例代码片段并思考“为什么?”但请相信我:这是有原因的。

代码如下:

class LinkedListNode
// blaa
{
public:
    LinkedListNode ( void* p )
    {
        // blaa
    }
} ;

template <typename T>
class InheritAndLinkList
:   public virtual T
,   public LinkedListNode
{
public:
    InheritAndLinkList ()
    :    LinkedListNode ( static_cast<void*>(static_cast<T*>(this)) ) // an exception occurs here when ..... (scroll down)
    { }
} ;

template <typename T>
class Implements
:   public virtual InheritAndLinkList<T>
{ } ;


class    A
{
public:
    virtual void goA () =0 ;
} ;

class    B
:   public Implements<A>
{
public:
    virtual void goB () =0 ;
} ;


class    MyClass
:   public Implements<B>
{
public:
    virtual void goA ()
    {
        // blaa
    }
    virtual void goB ()
    {
        // blaa
    }
} ;


int main ( ... )
{
    MyClass * p = new MyClass () ; // ..... This line executes

    p->goA() ;
    p->goB() ;

    return 0 ;
}

具体错误是,在构造时,表达式 static_cast<T*>(this)导致段错误....... 使用英特尔 C++ 编译器时。这已经在 GCC、LLVM、MS Visual Studio 等许多版本上工作了多年。现在 ICPC 让它死了。

我相信这是一件完全正确的事情。调用此行时,T已经构建并且应该可以有效使用...除非 C++ 规范中有关于此的另一件奇怪的事情。

static_cast在构造函数主体中(并更改其 super 以匹配)导致它避免此段错误。

所以我的问题是:规范中的哪个地方说这个[static cast] 是/不安全?

最佳答案

就其值(value)而言,代码对我来说看起来还不错。在使用 static_cast 时,我看不出有任何争议。 - 它是普通的派生到基指针转换。对我来说看起来像是一个编译器错误。

如果你坚持章节和诗歌:

[expr.static.cast]/4 An expression e can be explicitly converted to a type T using a static_cast of the form static_cast<T>(e) if the declaration T t(e); is well-formed, for some invented temporary variable t (8.5). The effect of such an explicit conversion is the same as performing the declaration and initialization and then using the temporary variable as the result of the conversion.

所以我们正在查看 T t(this); 的有效性在 InheritAndLinkList<T> 的构造函数中- 直接初始化:

[dcl.init]/17 ...

-- Otherwise, the initial value of the object being initialized is the (possibly converted) value of the initializer expression. Standard conversions (Clause 4) will be used, if necessary, to convert the initializer expression to the cv-unqualified version of the destination type; no user-defined conversions are considered.

.

[conv.ptr]/3 A prvalue of type “pointer to cv D”, where D is a class type, can be converted to a prvalue of type “pointer to cv B”, where B is a base class (Clause 10) of D. If B is an inaccessible (Clause 11) or ambiguous (10.2) base class of D, a program that necessitates this conversion is ill-formed. The result of the conversion is a pointer to the base class subobject of the derived class object.


编辑

经过激烈的评论讨论,使用this从构造函数初始化列表中获取并不是那么简单 - 但我相信您的特定用途仍然合法。

[class.cdtor]/3 To explicitly or implicitly convert a pointer (a glvalue) referring to an object of class X to a pointer (reference) to a direct or indirect base class B of X, the construction of X and the construction of all of its direct or indirect bases that directly or indirectly derive from B shall have started and the destruction of these classes shall not have completed, otherwise the conversion results in undefined behavior... [Example:

struct A { };
struct B : virtual A { };
struct C : B { };
struct D : virtual A { D(A*); };
struct X { X(A*); };

struct E : C, D, X {
  E() : D(this), // undefined: upcast from E* to A*
                 // might use path E* ! D* ! A*
                 // but D is not constructed
                 // D((C*)this), // defined:
                 // E* ! C* defined because E() has started
                 // and C* ! A* defined because
                 // C fully constructed
  X(this) {      // defined: upon construction of X,
                 // C/B/D/A sublattice is fully constructed
  }
};

end example ]

您的情况类似于 X(this)在上面的例子中,实际上比那个更简单,因为你只在层次结构中向上移动了一个步骤,所以没有需要关心的中间类。

关于C++ 虚拟继承 : static_cast "this" to virtual parent in initializer list of derived,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/38487640/

相关文章:

c++ - 如何为函数类型使用模板参数?

c++ - 为什么 MSVC 的自动矢量化不使用 AVX2?

C++自定义迭代器构造函数

c++ - 为什么 C 和 C++ 中 ` struct T{ double x};` 的输出不同?

c++ - 函数模板重载 - 特化

c++ - 查找 vtkImageData 来源和大小

Java - 具有预定义继承值的构造函数

java - 是否可以从多类中的构造函数重定向实例?

c++ - 我可以扩展一个参数包并用它定义一个参数列表吗?

c++ - 访问 float 的 4 个字节是否会破坏 C++ 别名规则