这种情况下虚函数能使用模板代替吗? - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
nyanyh
V2EX    C++

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

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

    有两个 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     Solana     2824 人在线   最高记录 6679       Select Language
    创意作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 25ms UTC 14:37 PVG 22:37 LAX 06:37 JFK 09:37
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86