【问题标题】:scalable SFINAE conditional override可扩展的 SFINAE 条件覆盖
【发布时间】:2019-03-09 00:19:13
【问题描述】:

上下文

我的情况与这里描述的类似:conditional (SFINAE) override

除了那个问题,只有一个函数被有条件地覆盖。我想知道如何获得一个可以为 n 个独立功能启用此功能的解决方案。对于链接问题中描述的答案,这将导致该类的 n^2 个版本(真/假组合)。

尝试

#include <cstdio>                                                                  
#include <type_traits>                                                             

struct A {                                                                         
    virtual void foo() { printf("A::foo()\n"); }                                   
    virtual void bar() { printf("A::bar()\n"); }                                   
    void print() { foo(); bar(); }                                                 
};                                                                                 

template <bool FOO, bool BAR>                                                      
struct B : public A {                                                              
    template <bool b = FOO>                                                        
    typename std::enable_if<b, void>::type                                         
    foo() override { printf("B::foo()\n"); }                                       

    template <bool b = BAR>                                                        
    typename std::enable_if<b, void>::type                                         
    bar() override { printf("B::bar()\n"); }                                       
};                                                                                 

int main() {                                                                       
    A *a = new B<true, false>();                                                   
    a->print();                                                                    

    return 0;                                                                      
}

但是,g++-8.1.0 似乎不会为重写的方法生成符号并调用A::{foo,bar}()

$ g++ -std=c++11 test.cc && ./a.out && nm -C a.out | grep -e foo -e bar 
A::foo()
A::bar()
00000000004006de W A::bar()
00000000004006c4 W A::foo()

clang++-6.0 抱怨:

error: only virtual member functions can be marked 'override'

他们修复了一个非常相似的错误:https://bugs.llvm.org/show_bug.cgi?id=13499。好的,所以我尝试不使用 override 关键字并获得与 g++ 相同的结果:

$ clang++ -std=c++11 test.cc && ./a.out && nm -C a.out | grep -e foo -e bar
A::foo()
A::bar()
0000000000400850 W A::bar()
0000000000400820 W A::foo()

两个编译器都未能在运行时完成任务,这让我相信我的示例做错了,或者我试图打破某个规则。请赐教。

目标

我们的目标是有一些东西可以覆盖基类中的任何虚拟方法组合,而无需为每个组合创建“版本”。注意:这应该在编译时完成。

【问题讨论】:

    标签: c++ c++11 overriding sfinae virtual-functions


    【解决方案1】:

    问题是模板函数不能是virtual

    我想到的最好的替代方法是创建一个 oFoo 模板结构,专门化,根据布尔模板值覆盖(或不覆盖)foo()

    template <bool>
    struct oFoo : virtual public A
     { void foo () override { std::cout << "B::foo()" << std::endl; } };
    
    template <>
    struct oFoo<false>
     { };
    

    然后是oBar 模板结构,用于bar()

    template <bool>
    struct oBar : virtual public A
     { void bar () override { std::cout << "B::bar()" << std::endl; } };
    
    template <>
    struct oBar<false>
     { };
    

    所以你可以写B如下

    template <bool FOO, bool BAR>
    struct B : virtual public A, public oFoo<FOO>, public oBar<BAR>
     { };
    

    注意virtual 继承以避免菱形问题。

    以下是完整的编译示例

    #include <iostream>
    #include <type_traits>                                                             
    struct A
     {                                                                         
       virtual void foo() { std::cout << "A::foo()" << std::endl; }
       virtual void bar() { std::cout << "A::bar()" << std::endl; }
       void print() { foo(); bar(); }
     };
    
    template <bool>
    struct oFoo : virtual public A
     { void foo () override { std::cout << "B::foo()" << std::endl; } };
    
    template <>
    struct oFoo<false>
     { };
    
    template <bool>
    struct oBar : virtual public A
     { void bar () override { std::cout << "B::bar()" << std::endl; } };
    
    template <>
    struct oBar<false>
     { };
    
    template <bool FOO, bool BAR>
    struct B : virtual public A, public oFoo<FOO>, public oBar<BAR>
     { };
    
    int main ()
     {
       A *a = new B<true, false>();
    
       a->print();
     }
    

    【讨论】:

    • 不错的方法;您已将代码复制从结构移至方法,这应该更少 LOC。因此,即使覆盖方法本身不是虚拟的,它仍然是不行的吗?另外,你为什么要为 B 虚拟继承 oFoo 和 oBar?似乎不需要。
    • @user10262178 - “所以即使覆盖方法本身不是虚拟的,它仍然是不行的?”嗯...不:他们override,所以他们是virtual。 “另外,你为什么要为 B 虚拟继承 oFoo 和 oBar?似乎不需要”我的错误:我不记得究竟在哪里需要虚拟继承;已删除。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-09-19
    • 2016-09-30
    • 1970-01-01
    • 2012-07-23
    • 2015-08-10
    • 1970-01-01
    相关资源
    最近更新 更多