【问题标题】:Issue refactoring curiously recurring template pattern问题重构奇怪地重复出现的模板模式
【发布时间】:2012-01-23 20:42:43
【问题描述】:

以下代码在 g++ 4.6.1 上编译失败:

template<class Base>
struct GetBase {
  Base * getBase() {
    return static_cast<Base *>(this);
  }
};

template<class Derived>
struct Parent : private GetBase<Derived> {
  using GetBase<Derived>::getBase;
  int y() {
    return getBase()->x();
  }
};

struct Child : public Parent<Child> {
  int x() {
    return 5;
  }
  int z() {
    return y();
  }
};

有错误

In member function ‘Base* GetBase<Base>::getBase() [with Base = Child]’:
    instantiated from ‘int Parent<Derived>::y() [with Derived = Child]’
    instantiated from here
error: ‘GetBase<Child>’ is an inaccessible base of ‘Child’

将 static_cast 更改为 reinterpret_cast 将使代码能够编译,并且在这种情况下可以正常工作,但我想知道这是否在所有情况下都是可接受的解决方案?即,指向基类的指针是否与此不同?我假设如果父母有数据成员,这可能会发生多重继承?如果GetBase是第一个超类,是否保证this指针相等?

【问题讨论】:

  • 我很震惊这个在 VS2010 上的编译......不管你应该重构你的设计。
  • 是的,在某些情况下 reinterpret_cast 会中断,其中大部分是完全由实现定义的。
  • 至少考虑删除getBase() 的无限层,并更喜欢dynamic_cast 来确定派生自基类指针。
  • 仅供参考的虚拟方法可能会简化这种疯狂的继承方案。
  • @AJG85- 这个设置(CRTP)的重点是避免动态多态性并在编译时处理所有事情。添加虚拟方法和dynamic_cast 会破坏设计的目的。

标签: c++ templates crtp


【解决方案1】:

我想知道这是否在所有情况下都是可接受的解决方案?

没有。(见下文)

是否有过指向基类的指针与 this 不同的情况?

是的。

  • 使用多重继承,不能期望基类具有相同的地址。

  • 根据编译器的不同,具有 vtable 指针的派生类可能与没有 vtable 指针的基类具有相同的this

当显式向上转换为基时,static_cast 是适当的 C++ 转换。

【讨论】:

    【解决方案2】:

    一个好问题;这让我对static_cast有了新的认识。

    我认为以下代码实现了您想要的:为 CRTP 提供了一个基类,该基类具有一个将 this 强制转换为 Derived 类型的成员函数,但只允许该基类的直接后代访问它。它使用 GCC 4.3.4 (tested at ideone) 和 Clang(在 llvm.org 测试)编译。抱歉,我忍不住更改了让我感到困惑的名称。

    #include <iostream>
    
    template<class Derived>
    class CRTP {
    protected:
      Derived * derived_this() {
        return static_cast<Derived *>(this);
      }
    };
    
    template<class Derived>
    struct Parent : public CRTP<Derived> {
    private:
      using CRTP<Derived>::derived_this;
    public:
      int y() {
        return derived_this()->x();
      }
    };
    
    struct Child : public Parent<Child> {
      int x() {
        return 5;
      }
      int z() {
        return y();
      }
    };
    
    int main() {
      std::cout << Child().z() << std::endl;
      return 0;
    }
    

    这个变体之所以有效,是因为继承是公共的,它支持将指向派生类的指针标准转换为指向基类的指针,以及使用 @ 进行逆转换(从基类到派生类) 987654325@,这是 CRTP 需要的。您的代码中的私有继承禁止这样做。所以我公开了继承,更改了要保护的方法,并在Parent 中通过将using 声明放入私有部分来进一步限制访问。

    【讨论】:

    • +1 并感谢您的回答。它为我节省了大量的调试工作。 MSVC++ 2012 Express 仍然使用私有继承编译...
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2016-01-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多