发出重构奇怪的重复模板模式

时间:2012-01-23 20:42:43

标签: c++ templates crtp

以下代码无法在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是第一个超类,这个指针是否保证相等?

2 个答案:

答案 0 :(得分:3)

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

否。(见下文)

是否一直指向基类的指针与此不一样?

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

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

当明确向上转换为基类时,static_cast是适当的C ++强制转换。

答案 1 :(得分:1)

一个好问题;它让我学到了关于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;
}

这种变体是有效的,因为继承是公共的,它允许将指向派生类的指针标准转换为指向基类的指针,以及使用{转换(从base到派生)的逆转换。 {1}},CRTP需要。您的代码中的私有继承禁止此操作。所以我将继承公开,更改了要保护的方法,并在static_cast中通过将Parent声明放入私有部分来进一步限制访问。