代码之家  ›  专栏  ›  技术社区  ›  cppBeginner

SFINAE:派生类隐藏基类函数依赖于T

  •  1
  • cppBeginner  · 技术社区  · 7 年前

    我编写了两个SFINAE代码片段。
    他们做完全相同的事情。
    然而,第一个有效,而第二个无效。

    此代码有效( http://coliru.stacked-crooked.com/a/50e07af54708f076 )

    #include <iostream>
    #include  <type_traits>
    enum EN{ EN1,EN2 };
    template<EN T1> class B{
        public: template<EN enLocal=T1> typename     
          std::enable_if<enLocal==EN1,void>::type test(){  std::cout<<"1"<<std::endl;}  
        public: template<EN enLocal=T1> typename    
          std::enable_if<enLocal==EN2,void>::type test(){  std::cout<<"2"<<std::endl; }
    };
    
    int main(){
        B<EN1> b;
        b.test(); 
    }
    

    但是这个代码是不可编译的( http://coliru.stacked-crooked.com/a/28b6afd443b36c7e

    #include <iostream>
    #include  <type_traits>
    enum EN{ EN1,EN2 };
    class Base{
        public: void test(){
            std::cout<<"1"<<std::endl;
        };
    };
    template<EN T1> class B : public Base{
        public: template<EN enLocal=T1> 
          std::enable_if_t< enLocal==EN2,void > test(){
            std::cout<<"2"<<std::endl;
        }
    };
    int main(){
        B<EN1> bEn1; bEn1.test(); //should print 1
        //B<EN2> bEn2; bEn2.test(); //print 2
    }
    

    我对SFINAE很陌生,现在仍然通过网络学习 https://stackoverflow.com/a/50562202/ .

    2 回复  |  直到 7 年前
        1
  •  3
  •   user7860670    7 年前

    该代码有两个问题:

    1. bEn1.test(); bEn2.test(); 编译器将计算出该名称 test 指类中的函数 B B::test . 这可以通过将基类中的名称引入派生类来解决:
    template<EN T1> class B : public Base{
        public: using Base::test;
    
    1. 然而,现在非模板函数将优先于模板函数(即使在 enable_if 工作)所以 B<EN2> bEn2; bEn2.test(); 将打印1。

    Base::test 将名称转换为派生类:

    public: template<EN enLocal=T1> 
        std::enable_if_t< enLocal!=EN2,void > test(){
           return Base::test();
    }
    

    if constexpr 而不是类型特征或SFINAE:

    public: template<EN enLocal = T1> void
    test()
    {
        if constexpr(EN2 == enLocal)
        {
            std::cout<<"2"<<std::endl;
        }
        else
        {
             Base::test();
        }
    }
    
        2
  •  1
  •   Bob__    7 年前

    根据实际使用情况,您也可以考虑某种形式的标签调度:

    enum class En {
        base, a, b, c
    };
    
    template<En Type> void test_impl()
    {
        if constexpr (Type == En::base)
            std::cout << "Base\n";
        else if constexpr (Type == En::a)
            std::cout << "1\n";
        else if constexpr (Type == En::b)
            std::cout << "2\n";
        else
            std::cout << "Default\n";
    }
    
    struct Base {
        void test() {
            std::cout << "Base - ";
            test_impl<En::base>();  
        }
    };
    
    template<En Type>
    struct Derived : public Base {
        void test() {
            std::cout << "Derived - ";
            test_impl<Type>();
        }  
    };
    
    int main()
    {
        Base b;
        b.test();              // -> "Base - Base"
        Derived<En::a> b1;
        b1.test();             // -> "Derived - 1" 
        Derived<En::b> b2;
        b2.test();             // -> "Derived - 2"
        Derived<En::base> b3;
        b3.test();             // -> "Derived - Base"
        Derived<En::c> b4;
        b4.test();             // -> "Derived - Default"
    }