使用当前类专门继承模板化函数

时间:2015-05-28 12:14:31

标签: c++ templates inheritance

最好跳过代码并阅读评论,以便快速了解我尝试做的事情,但是我需要尝试描述更多重要细节:

我想将一个成员函数添加到通过类heirarchy继承的类中。我想这样做,以便派生类不必重新键入任何代码或做任何特殊的事情来自动获取此功能,我想避免使用宏。此函数也应该由编译后生成的任何子类继承(这是其他人将扩展的平台的一部分)。

该函数是一个调试函数,它跟踪指向类实例的自定义智能指针(很像std :: shared_ptr) - 我希望能够找出为什么给定实例被保存在内存,这意味着我需要知道指向它的指针。定制的智能指针是模板化的,我想强制使用用作参数的智能指针的特化与实例的静态类型的类型匹配。

由于这是在一个框架中,因此在编译后为扩展层次结构的人启用此功能是非常重要的。

这是使用Visual Studio 2010在Windows上编译的。我们很快就会转向VS 2015,但我不想等待。我们也主要是跨平台(我们只在必要时为其他平台关闭了一些东西)。如果有一个解决方案需要C ++ 11/14/17 /更高版本,我仍然希望听到它的兴趣。

我觉得我需要同时制作模板化和专业化的功能,专门用某种类型的“自我”,但我不知道如何让子类获得基本上复制粘贴的内容而不是继承的代码。我也理解(我认为)这个函数不适合虚拟化,但我也想阻止子类获取父类'专业化。

我认为我可以选择一些选项,但我想知道我有没有想过的更好的事情:

  • 放弃尝试实现这种安全
  • 使用动态检查 - 我还没有弄清楚如何做到这一点。我认为这会更容易,但不太安全,我喜欢安全。
  • 使用宏自动为self创建一个typdef,ala https://stackoverflow.com/a/21149648/78823
    • 我不确定这是如何运作的。无论哪种方式,它只是问题的一半,我不认为这让我更接近解决函数声明/继承 - 但不是真正的难题
    • 也不是强迫人们使用宏的粉丝,特别是对于那些仅用于调试的东西。

我完全期望完全不同的设计模式是最好的解决方案,但我还没有想到这样的解决方案。

我真的认为编译器能够知道我需要它支持的所有内容,但是我不确定语言是否会这样做。

我也非常感谢有关如何更好地说出这个问题的一些帮助或反馈。我自己很难解决这个问题,更不用说为别人理解了。

template<class T>
class CustomSmartPointer {};

class Base {
public:
    void doSomething(CustomSmartPointer<Base> arg) {} //Should be able to say something like CustomSmartPointer<Self> instead, where the compiler knows what I mean by Self
};

class Derived : public Base {
public:
    void doSomething(CustomSmartPointer<Derived> arg) {} //Shouldn't have to specify this declaration, or code, again, as it is direcly copy-pastable from above
};

class Derived2 : public Base {};

void main() {
    Base b;
    Derived d;
    Derived2 d2;

    CustomSmartPointer<Base> cb;
    CustomSmartPointer<Derived> cd;

    b.doSomething(cb);

    d.doSomething(cd);

    d2.doSomething(cb); //This shouldn't compile, as cb is the wrong type for the specialisation that should exist for Derived2::doSomething
}

3 个答案:

答案 0 :(得分:2)

我将重复我的评论,这解释了为什么运行时继承不适合:void doSomething(CustomSmartPointer<Base> arg)void doSomething(CustomSmartPointer<Derived> arg)是不相关的成员函数(尽管它们是{{1}内的同名重载{1}})。如果您不希望在Derived中使用void doSomething(CustomSmartPointer<Base> arg),则在您的情况下运行时继承是不合适的。

CRTP可能是您正在寻找的。

Derived

当然,这意味着派生类不会有共同的父级。您可以为template <class T> struct Base { void doSomething(CustomSmartPointer<T>); }; struct Derived: Base<Derived> {}; // will inherit void doSomething(CustomSmartPointer<Derived>) from parent struct Derived2: Base<Derived2> {}; // will inherit void doSomething(CustomSmartPointer<Derived2>) from parent 非模板父级提供解决方法,但不能将其用作Base的接口。

另一方面,如果你不能修改doSomethingBase必须继承Derived那么就没有办法避免继承成员函数,如果它是一个bug称之为,抓住错误编译时间会很棘手。但是,您可以覆盖Base中的doSomething(CustomSmartPointer<Base>)并抛出运行时异常以捕获错误运行时。

答案 1 :(得分:1)

实现这一目标的一种可能方法可能是CRTP,尽管它可能看起来很难看并且使继承复杂化:

template<class T>
class CustomSmartPointer {};

template <class D>
class Base {
public:
    void doSomething(CustomSmartPointer<D> arg) {} 
};

class Derived : public Base<Derived> {};

class Derived2 : public Base<Derived2> {};

int main() {
    Derived d;
    Derived2 d2;

    CustomSmartPointer<Derived> cd;

    d.doSomething(cd);

    d2.doSomething(cd); //does not compile
    return 0;
}

答案 2 :(得分:0)

当您使用模板编译c ++文件时会发生什么,编译器会在需要时创建专用模板函数。 (这是你错误中的instantiated from here)。 在您的情况下,编译器将创建两个类:CustomSmartPointerBaseCustomSmartPointerDerived。 现在你在base:doSomething(CustomSmartPointerBase)中创建一个函数。此函数也可在Derived类中使用,因为它是一个子类。 doSomething中的Derived声明不会覆盖Base中的Derived声明,因为它采用不同的类型作为参数,因此它会过载。

这意味着doSomething类中包含两个函数:CustomSmartPointer用于cout << "doSomethingBase" << endl;

您可以通过在相应的cout << "doSomethingDerived" <<endl;功能中添加doSomething和{{1}}语句并运行代码来尝试此操作。或者更好,使用调试器逐步完成它。