【问题标题】:Crash due to mixing CRTP & interfaces?由于混合 CRTP 和接口而崩溃?
【发布时间】:2020-08-13 10:37:06
【问题描述】:

我正在试验 CRTP 并将其与接口混合,但我无法解释为什么该程序会崩溃(在 Clang、GCC 和 MSVC 中)。在最新的 Clang 和 GCC 中,它使用 -Wall、-Wextra 构建时不会发出警告。我的猜测是虚拟方法调用没有解决,但我无法解释原因(如果从接口中删除 GetInt() 不会崩溃)。在我的调试器中,我看到崩溃发生在static_cast<T*>(this)->GetInt() 行。

#include <iostream>

class INode
{
public:
    virtual int GetInt() = 0;
protected:
    ~INode() {}
};

template<template<class> typename NodeBase>
class Node : public INode, public NodeBase<Node<NodeBase>>
{
public:
    int GetInt() override { return 42; }
};

template<typename T>
class MyNodeBase
{
public:
    int CallGetInt() {
        return static_cast<T*>(this)->GetInt();
    }
};

template<template<class> typename NodeBase>
int Foo1(Node<NodeBase> n) {
    return n.CallGetInt();
}

template<typename T>
int Foo2(MyNodeBase<T> n) {
    return n.CallGetInt();
}

int main() {
    Node<MyNodeBase> n;
    std::cout << Foo1(n) << std::endl; // ok
    std::cout << Foo2(n) << std::endl; // crash
}

【问题讨论】:

    标签: c++ interface crash crtp


    【解决方案1】:

    您是 slicing n 来电至 Foo2

    Foo2 按值接受其参数。这意味着nMyNodeBase&lt;Node&lt;MyNodeBase&gt;&gt; 子对象的副本 是传递给Foo2 的内容。由于Foo2 中的n 不是Node&lt;MyNodeBase&gt;,因此通过CallGetInt 中的转换返回的指针调用GetInt 会导致您的程序表现出未定义的行为。

    如果您更改Foo2 以通过引用接受其参数,您的程序的行为将是明确定义的。

    【讨论】:

      【解决方案2】:

      Foo2 参数按值传递。所以完整的对象n 的类型是MyNodeBase&lt;Node&lt;MyNodeBase&gt;&gt;。它不是Node&lt;MyNodeBase&gt; 的派生对象。但是表达式static_cast&lt;T*&gt;(this) 假定this 是从Node&lt;MyNodeBase&gt; 派生的,因此static_cast&lt;T*&gt;(this)-&gt;GetInt() 是未定义的行为。

      你避免了这个错误:

      template<typename T>
      class MyNodeBase
      {
      protected:
         MyNodeBase() = default;
         MyNodeBase(const MyNodeBase&) = default;
         MyNodeBase& operator = (const MyNodeBase&) = default;
      
      public:
          
          int CallGetInt() {
              return static_cast<T*>(this)->GetInt();
          }
      };
      

      【讨论】:

        猜你喜欢
        • 2011-11-09
        • 1970-01-01
        • 2023-02-02
        • 2015-06-07
        • 1970-01-01
        • 2011-02-17
        • 2011-03-12
        • 2015-09-08
        相关资源
        最近更新 更多