V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
nyanyh
V2EX  ›  C++

这种情况下虚函数能使用模板代替吗?

  •  
  •   nyanyh · 2019-12-15 11:02:49 +08:00 · 2999 次点击
    这是一个创建于 1565 天前的主题,其中的信息可能已经有所发展或是发生改变。

    有两个 A、B 两个抽象类分别派生出几个类,我想在一类中加一个指针指向另一类的实例。大致代码如下:

    class parentA {
    public:
        virtual void func() = 0;
    
        parentB* ptrToAnother; // here
    };
    
    class classA1 : public parentA {
    public:
        void func() override;
    };
    
    class classA2 : public parentA {
    public:
        void func() override;
    };
    
    
    class parentB {
    public:
        virtual void func() = 0;
    
        parentA* ptrToAnother; // here
    };
    
    class classB1 : public parentB {
    public:
        void func() override;
    };
    
    class classB2 : public parentB {
    public:
        void func() override;
    };
    
    
    classB1 b1;
    classA1 a1;
    a1.ptrToAnother = &b1;
    b1.ptrToAnother = &a1;
    
    a1.ptrToAnother->func();
    b1.ptrToAnother->func();
    
    

    这样就可以通过 ptrToAnother 指针访问另一个类。但是想换成模板实现就爆炸了:

    template<typename AnotherClassName>
    class parentA {
    public:
        virtual void func() = 0;
    
        AnotherClassName* ptrToAnother; // here
    };
    
    template<typename AnotherClassName>
    class classA1 : public parentA<AnotherClassName> {
    public:
        void func() override;
    };
    
    template<typename AnotherClassName>
    class classA2 : public parentA<AnotherClassName> {
    public:
        void func() override;
    };
    
    
    template<typename AnotherClassName>
    class parentB {
    public:
        virtual void func() = 0;
    
        AnotherClassName* ptrToAnother; // here
    };
    
    template<typename AnotherClassName>
    class classB1 : public parentB<AnotherClassName> {
    public:
        void func() override;
    };
    
    template<typename AnotherClassName>
    class classB2 : public parentB<AnotherClassName> {
    public:
        void func() override;
    };
    
    
    // 不能编译,缺少模板参数
    classA1<classB1> a1;
    classB1<classA1> b1;
    
    a1.ptrToAnother->func();
    b1.ptrToAnother->func();
    
    
    第 1 条附言  ·  2019-12-15 14:11:51 +08:00

    测试了下这样写能编译,也能正常调用,就是不知道有什么问题

    DerivedA<DerivedB<void>> A;
    DerivedB<DerivedA<void>> B;
    
    A.prtToPair = reinterpret_cast<DerivedB<void> *>(&B);
    B.prtToPair = reinterpret_cast<DerivedA<void> *>(&A);
    
    // it works
    A.ptrToPair->someFunc();
    B.ptrToPair->someFunc();
    

    然后又在StackOverflow上问了下 https://stackoverflow.com/questions/59341379/c-how-to-solve-template-cyclic-dependency-between-derived-classes-when-two-cl

    有人提供了一种先拿模板包装的想法:

    template<template<typename> typename F, template<typename> typename... Fs>
    struct Fix {
        F<Fix<Fs..., F>> unwrapped;
    };
    
    Fix<DerivedA, DerivedB> dA;
    Fix<DerivedB, DerivedA> dB;
    dA.unwrapped.prtToPair = &dB;
    dB.unwrapped.prtToPair = &dA;
    
    dA.unwrapped.prtToPair->unwrapped.func();
    dB.unwrapped.prtToPair->unwrapped.func();
    

    这样的问题就是需要调用两次unwrapped,看起来不习惯

    7 条回复    2019-12-16 10:43:02 +08:00
    gwy15
        1
    gwy15  
       2019-12-15 13:05:07 +08:00
    用模板,ClassA1 的模板参数得是一个具体的类而不是模板类,这里的依赖变成了
    type(a1) = classA1<type(b1)> = classA1<classB1<type(a1)>>
    所以构成了循环依赖,模板系统里这个方程的解是不存在的,模板没法展开。

    如果不关心具体的实现,这样可以通过编译。但是调用的是 derived<int>,不是你想要的那个结果
    ```c++
    classA1<classB1<int>> a1;
    classB1<classA1<int>> b1;

    a1.ptrToAnother = new classB1<int>();
    b1.ptrToAnother = new classA1<int>();

    a1.ptrToAnother->func();
    b1.ptrToAnother->func();
    ```
    amai0w0
        2
    amai0w0  
       2019-12-15 13:23:50 +08:00 via Android
    在 template 里边的变量必须是特化的,如果里面是另一个另一个 template 类的话什么应该是 template<template<typename> class AntherClassName,typename E>, 然后用 AntherClassName<E>调用。但是这里的 classB1 又是用 classA1 特化,感觉无解了。所以我觉得还是得像前面那样实例化以后再注入。
    插眼等一个 dalao
    nyanyh
        3
    nyanyh  
    OP
       2019-12-15 13:33:00 +08:00
    @amai0w0 #2
    @gwy15 #1
    刚才发现了一种非常神奇的写法 http://qscribble.blogspot.com/2008/06/circular-template-references-in-c.html



    但是适用于两个固定的类 A 和 B,我这种情况有两堆派生类,如果给 MyCombo 加模板的话就又产生循环依赖的问题了
    secondwtq
        4
    secondwtq  
       2019-12-15 14:06:29 +08:00
    试试用 #3 的方法加上 #2 的 HKT

    template <template<typename> class A, template<typename> class B>
    struct MyCombo {
    typedef A<MyCombo<A, B>> a_t;
    typedef B<MyCombo<A, B>> b_t;
    };

    Aa<MyCombo<Aa, Bb>> a;
    Bb<MyCombo<Aa, Bb>> b;
    nyanyh
        5
    nyanyh  
    OP
       2019-12-15 14:16:23 +08:00
    @secondwtq #4 声明变量可以编译,但是给 prtToAnother 赋值就会出错了
    a.prtToPair = &b;
    ^~
    error: assigning to 'MyCombo<DerivedA, DerivedB> *' from incompatible type 'DerivedB<MyCombo<DerivedA, DerivedB> > *'
    secondwtq
        6
    secondwtq  
       2019-12-15 14:19:23 +08:00
    @nyanyh 你 check 下代码,你这个“prtToPair”看起来是个 MyCombo * ...
    wutiantong
        7
    wutiantong  
       2019-12-16 10:43:02 +08:00
    模版对应编译时多态,继承对应运行时多态,

    你走的路子还是继承的,非要用模版来掺一脚令人费解啊。
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   我们的愿景   ·   实用小工具   ·   955 人在线   最高记录 6543   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 103ms · UTC 21:24 · PVG 05:24 · LAX 14:24 · JFK 17:24
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.